Merge branch 'FF/Ops' into FF/OpsDev

This commit is contained in:
Frank
2025-11-23 20:47:49 +01:00
21 changed files with 2326 additions and 964 deletions
+1 -1
View File
@@ -1475,7 +1475,7 @@ function EVENT:onEvent( Event )
-- SCENERY
---
Event.TgtDCSUnit = Event.target
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit.getName() or nil
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit:getName() or nil
if Event.TgtDCSUnitName~=nil then
Event.TgtUnitName = Event.TgtDCSUnitName
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
-170
View File
@@ -3,8 +3,6 @@
-- ## 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.
--
-- ===
--
@@ -3892,171 +3890,3 @@ do -- COORDINATE
end
end
do
--- The POINT_VEC3 class
-- @type POINT_VEC3
-- @field #number x The x coordinate in 3D space.
-- @field #number y The y coordinate in 3D space.
-- @field #number z The z COORDINATE in 3D space.
-- @field Utilities.Utils#SMOKECOLOR SmokeColor
-- @field Utilities.Utils#FLARECOLOR FlareColor
-- @field #POINT_VEC3.RoutePointAltType RoutePointAltType
-- @field #POINT_VEC3.RoutePointType RoutePointType
-- @field #POINT_VEC3.RoutePointAction RoutePointAction
-- @extends #COORDINATE
--- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.
--
-- **DEPRECATED - PLEASE USE COORDINATE!**
--
-- **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,
-- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors,
-- who you can find on the Eagle Dynamics Forums.
--
--
-- ## POINT_VEC3 constructor
--
-- A new POINT_VEC3 object can be created with:
--
-- * @{#POINT_VEC3.New}(): a 3D point.
-- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCS#Vec3}.
--
--
-- ## Manupulate the X, Y, Z coordinates of the POINT_VEC3
--
-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively.
-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value.
-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:
--
-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
--
--
-- ## 3D calculation methods
--
-- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:
--
--
-- ## Point Randomization
--
-- Various methods exist to calculate random locations around a given 3D point.
--
-- * @{#POINT_VEC3.GetRandomPointVec3InRadius}(): Provides a random 3D point around the current 3D point, in the given inner to outer band.
--
--
-- @field #POINT_VEC3
POINT_VEC3 = {
ClassName = "POINT_VEC3",
Metric = true,
RoutePointAltType = {
BARO = "BARO",
},
RoutePointType = {
TakeOffParking = "TakeOffParking",
TurningPoint = "Turning Point",
},
RoutePointAction = {
FromParkingArea = "From Parking Area",
TurningPoint = "Turning Point",
},
}
--- RoutePoint AltTypes
-- @type POINT_VEC3.RoutePointAltType
-- @field BARO "BARO"
--- RoutePoint Types
-- @type POINT_VEC3.RoutePointType
-- @field TakeOffParking "TakeOffParking"
-- @field TurningPoint "Turning Point"
--- RoutePoint Actions
-- @type POINT_VEC3.RoutePointAction
-- @field FromParkingArea "From Parking Area"
-- @field TurningPoint "Turning Point"
-- Constructor.
--- Create a new POINT_VEC3 object.
-- @param #POINT_VEC3 self
-- @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 )
local self = BASE:Inherit( self, COORDINATE:New( x, y, z ) ) -- Core.Point#POINT_VEC3
self:F2( self )
return self
end
end
do
--- @type POINT_VEC2
-- @field DCS#Distance x The x coordinate in meters.
-- @field DCS#Distance y the y coordinate in meters.
-- @extends Core.Point#COORDINATE
--- Defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
--
-- **DEPRECATED - PLEASE USE COORDINATE!**
--
-- ## POINT_VEC2 constructor
--
-- A new POINT_VEC2 instance can be created with:
--
-- * @{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
--
-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively.
-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value.
-- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively.
-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:
--
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
--
-- @field #POINT_VEC2
POINT_VEC2 = {
ClassName = "POINT_VEC2",
}
--- POINT_VEC2 constructor.
-- @param #POINT_VEC2 self
-- @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 )
local LandHeight = land.getHeight( { ["x"] = x, ["y"] = y } )
LandHeightAdd = LandHeightAdd or 0
LandHeight = LandHeight + LandHeightAdd
local self = BASE:Inherit( self, COORDINATE:New( x, LandHeight, y ) ) -- Core.Point#POINT_VEC2
self:F2( self )
return self
end
end
+2 -1
View File
@@ -945,7 +945,8 @@ do -- SET_BASE
function SET_BASE:IsInSet(Object)
--self:F3(Object)
local outcome = false
local name = Object:GetName()
if Object == nil then return false end
local name = (Object ~= nil and Object.GetName) and Object:GetName() or "none"
--self:I("SET_BASE: Objectname = "..name)
self:ForEach(
function(object)
+1 -1
View File
@@ -1179,7 +1179,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
self.ScanData.SceneryTable = {}
self.ScanData.Units = {}
local ZoneCoord = self:GetCoordinate()
local ZoneCoord = self:GetCoordinate():SetAlt()
local ZoneRadius = self:GetRadius()
--self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
@@ -0,0 +1,999 @@
--- **Functional** - 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.
--
-- ===
--
-- ## Additional Material:
--
-- * **Demo Missions:** [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/FORMATION)
-- * **Guides:** None
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions: **Applevangelist** (refactor)
--
-- ===
--
-- @module Functional.FORMATION
-- @image AI_Large_Formations.JPG
--- FORMATION class
-- @type FORMATION
-- @extends Core.Fsm#FSM_SET
-- @field Wrapper.Unit#UNIT FollowUnit
-- @field Core.Set#SET_GROUP FollowGroupSet
-- @field #string FollowName
-- @field Core.Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class.
-- @field #number FollowDistance The current follow distance.
-- @field DCS#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
-- @field DCS#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
-- @field #number dtFollow Time step between position updates.
--- Build (large) formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
--
-- FORMATION makes AI @{Wrapper.Group#GROUP}s fly in formation of various compositions.
-- The 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 FORMATION is not perfect, but is very useful to:
--
-- * Model large formations when flying straight line. You can build close formations when doing this.
-- * Make humans guide a large formation, when the planes are wide from each other.
--
-- ## FORMATION construction
--
-- Create a new SPAWN object with the @{#FORMATION.New} method:
--
-- * @{#FORMATION.New}(): Creates a new 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:
--
-- * @{#FORMATION.FormationLine}(): Form a line formation (core formation function).
-- * @{#FORMATION.FormationTrail}(): Form a trail formation.
-- * @{#FORMATION.FormationLeftLine}(): Form a left line formation.
-- * @{#FORMATION.FormationRightLine}(): Form a right line formation.
-- * @{#FORMATION.FormationRightWing}(): Form a right wing formation.
-- * @{#FORMATION.FormationLeftWing}(): Form a left wing formation.
-- * @{#FORMATION.FormationCenterWing}(): Form a center wing formation.
-- * @{#FORMATION.FormationCenterVic}(): Form a Vic formation (same as CenterWing).
-- * @{#FORMATION.FormationCenterBoxed}(): Form a center boxed formation.
--
-- ## Randomization
--
-- Use the method @{FORMATION#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()
-- local LeaderUnit = UNIT:FindByName( "Leader" )
-- local LargeFormation = FORMATION:New( LeaderUnit, FollowGroupSet, "Center Wing Formation", "Briefing" )
-- LargeFormation:FormationCenterWing( 500, 50, 0, 250, 250 )
-- LargeFormation:__Start( 1 )
--
-- @field #FORMATION
FORMATION = {
ClassName = "FORMATION",
FollowName = nil, -- The Follow Name
FollowUnit = nil,
FollowGroupSet = nil,
FollowScheduler = nil,
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
dtFollow = 0.5,
}
--- @type FORMATION.Formation
-- @field #number None
-- @field #number Line
-- @field #number Trail
-- @field #number Stack
-- @field #number LeftLine
-- @field #number RightLine
-- @field #number LeftWing
-- @field #number RightWing
-- @field #number Vic
-- @field #number Box
FORMATION.Formation = {
None = 1,
Line = 2,
Trail = 3,
Stack = 4,
LeftLine = 5,
RightLine = 6,
LeftWing = 7,
RightWing = 8,
Vic = 9,
Box = 10,
}
--- FORMATION class constructor for an AI group
-- @param #FORMATION self
-- @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 #FORMATION self
function FORMATION:New( FollowUnit, FollowGroupSet, FollowName )
local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) )
self:F( { FollowUnit, FollowGroupSet, FollowName } )
self.FollowUnit = FollowUnit -- Wrapper.Unit#UNIT
self.FollowGroupSet = FollowGroupSet -- Core.Set#SET_GROUP
self:SetFlightRandomization( 2 )
self:SetStartState( "None" )
self:AddTransition( "*", "Stop", "Stopped" )
self:AddTransition( {"None", "Stopped"}, "Start", "Following" )
self:AddTransition( "*", "FormationLine", "*" )
--- Start Trigger for FORMATION
-- @function [parent=#FORMATION] Start
-- @param #FORMATION self
--- Start Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __Start
-- @param #FORMATION self
-- @param #number Delay
--- Stop Trigger for FORMATION
-- @function [parent=#FORMATION] Stop
-- @param #FORMATION self
--- Stop Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __Stop
-- @param #FORMATION self
-- @param #number Delay
--- FormationLine Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationLine
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLine Trigger for FORMATION
-- @function [parent=#FORMATION] FormationLine
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLine Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationLine
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationTrail", "*" )
--- FormationTrail Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationTrail
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
--- FormationTrail Trigger for FORMATION
-- @function [parent=#FORMATION] FormationTrail
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
--- FormationTrail Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationTrail
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
self:AddTransition( "*", "FormationStack", "*" )
--- FormationStack Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationStack
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
--- FormationStack Trigger for FORMATION
-- @function [parent=#FORMATION] FormationStack
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
--- FormationStack Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationStack
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
self:AddTransition( "*", "FormationLeftLine", "*" )
--- FormationLeftLine Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationLeftLine
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftLine Trigger for FORMATION
-- @function [parent=#FORMATION] FormationLeftLine
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftLine Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationLeftLine
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationRightLine", "*" )
--- FormationRightLine Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationRightLine
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightLine Trigger for FORMATION
-- @function [parent=#FORMATION] FormationRightLine
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightLine Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationRightLine
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationLeftWing", "*" )
--- FormationLeftWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationLeftWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftWing Trigger for FORMATION
-- @function [parent=#FORMATION] FormationLeftWing
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftWing Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationLeftWing
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationRightWing", "*" )
--- FormationRightWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationRightWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightWing Trigger for FORMATION
-- @function [parent=#FORMATION] FormationRightWing
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightWing Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationRightWing
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationCenterWing", "*" )
--- FormationCenterWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationCenterWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationCenterWing Trigger for FORMATION
-- @function [parent=#FORMATION] FormationCenterWing
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationCenterWing Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationCenterWing
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationVic", "*" )
--- FormationVic Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationVic
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationVic Trigger for FORMATION
-- @function [parent=#FORMATION] FormationVic
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationVic Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationVic
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationBox", "*" )
--- FormationBox Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationBox
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
--- FormationBox Trigger for FORMATION
-- @function [parent=#FORMATION] FormationBox
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
--- FormationBox Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationBox
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
self:AddTransition( "*", "Follow", "Following" )
self:FormationLeftLine( 500, 0, 250, 250 )
self.FollowName = FollowName
self.CT1 = 0
self.GT1 = 0
return self
end
--- Set time interval between updates of the formation.
-- @param #FORMATION self
-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds.
-- @return #FORMATION
function FORMATION:SetFollowTimeInterval(dt)
self.dtFollow=dt or 0.5
return self
end
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
-- This allows to visualize where the escort is flying to.
-- @param #FORMATION self
-- @param #boolean SmokeDirection If true, then the direction vector will be smoked.
-- @return #FORMATION
function FORMATION:TestSmokeDirectionVector( SmokeDirection )
self.SmokeDirectionVector = ( SmokeDirection == true ) and true or false
return self
end
--- FormationLine Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation )
self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation } )
XStart = XStart or self.XStart
XSpace = XSpace or self.XSpace
YStart = YStart or self.YStart
YSpace = YSpace or self.YSpace
ZStart = ZStart or self.ZStart
ZSpace = ZSpace or self.ZSpace
local FollowSet = FollowGroupSet:GetSet()
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
local PointVec3 = COORDINATE:New()
PointVec3:SetX( XStart + i * XSpace )
PointVec3:SetY( YStart + i * YSpace )
PointVec3:SetZ( ZStart + i * ZSpace )
local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", Formation )
end
return self
end
--- FormationTrail Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @return #FORMATION
function FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0, self.Formation.Trail )
return self
end
--- FormationStack Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0, self.Formation.Stack )
return self
end
--- FormationLeftLine Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace, self.Formation.LeftLine )
return self
end
--- FormationRightLine Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.Formation.RightLine)
return self
end
--- FormationLeftWing Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.Formation.LeftWing)
return self
end
--- FormationRightWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationRightWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.Formation.RightWing)
return self
end
--- FormationCenterWing Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
local FollowSet = FollowGroupSet:GetSet()
local i = 0
for FollowID, FollowGroup in pairs( FollowSet ) do
local PointVec3 = COORDINATE:New()
local Side = ( i % 2 == 0 ) and 1 or -1
local Row = i / 2 + 1
PointVec3:SetX( XStart + Row * XSpace )
PointVec3:SetY( YStart )
PointVec3:SetZ( Side * ( ZStart + i * ZSpace ) )
local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", self.Formation.Vic )
end
return self
end
--- FormationVic Handle for FORMATION
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
self:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
return self
end
--- FormationBox Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #FORMATION
function FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels )
local FollowSet = FollowGroupSet:GetSet()
local i = 0
for FollowID, FollowGroup in pairs( FollowSet ) do
local PointVec3 = COORDINATE:New()
local ZIndex = i % ZLevels
local XIndex = math.floor( i / ZLevels )
local YIndex = math.floor( i / ZLevels )
PointVec3:SetX( XStart + XIndex * XSpace )
PointVec3:SetY( YStart + YIndex * YSpace )
PointVec3:SetZ( -ZStart - (ZSpace * ZLevels / 2 ) + ZSpace * ZIndex )
local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", self.Formation.Box )
end
return self
end
--- Use the method @{FORMATION#FORMATION.SetFlightRandomization}() to make the air units in your formation randomize their flight a bit while in formation.
-- @param #FORMATION self
-- @param #number FlightRandomization The formation flying errors that pilots can make while in formation. Is a range set in meters.
-- @return #FORMATION
function FORMATION:SetFlightRandomization( FlightRandomization )
self.FlightRandomization = FlightRandomization
return self
end
--- Stop function. Formation will not be updated any more.
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function FORMATION:onafterStop(FollowGroupSet, From, Event, To)
self:E("Stopping formation.")
end
--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To )
if From=="Stopped" then
return false -- Deny transition.
end
return true
end
--- Enter following state.
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function FORMATION:onenterFollowing( FollowGroupSet )
if self.FollowUnit:IsAlive() then
local ClientUnit = self.FollowUnit
local CT1, CT2, CV1, CV2
CT1 = ClientUnit:GetState( self, "CT1" )
local CuVec3=ClientUnit:GetVec3()
if CT1 == nil or CT1 == 0 then
ClientUnit:SetState( self, "CV1", CuVec3)
ClientUnit:SetState( self, "CT1", timer.getTime() )
else
CT1 = ClientUnit:GetState( self, "CT1" )
CT2 = timer.getTime()
CV1 = ClientUnit:GetState( self, "CV1" )
CV2 = CuVec3
ClientUnit:SetState( self, "CT1", CT2 )
ClientUnit:SetState( self, "CV1", CV2 )
end
--FollowGroupSet:ForEachGroupAlive( bla, self, ClientUnit, CT1, CV1, CT2, CV2)
for _,_group in pairs(FollowGroupSet:GetSet()) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
self:FollowMe(group, ClientUnit, CT1, CV1, CT2, CV2)
end
end
self:__Follow( -self.dtFollow )
end
end
--- Follow me.
-- @param #FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup Follow group.
-- @param Wrapper.Unit#UNIT ClientUnit Client Unit.
-- @param DCS#Time CT1 Time
-- @param DCS#Vec3 CV1 Vec3
-- @param DCS#Time CT2 Time
-- @param DCS#Vec3 CV2 Vec3
function FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
if not self:Is("Stopped") then
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
FollowGroup:OptionROTEvadeFire()
FollowGroup:OptionROEReturnFire()
local GroupUnit = FollowGroup:GetUnit( 1 )
local GuVec3=GroupUnit:GetVec3()
local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
if FollowFormation then
local FollowDistance = FollowFormation.x
local GT1 = GroupUnit:GetState( self, "GT1" )
if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
GroupUnit:SetState( self, "GV1", GuVec3)
GroupUnit:SetState( self, "GT1", timer.getTime() )
else
local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
local CT = CT2 - CT1
local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6
local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
local Ca = math.atan2( CDv.x, CDv.z )
local GT1 = GroupUnit:GetState( self, "GT1" )
local GT2 = timer.getTime()
local GV1 = GroupUnit:GetState( self, "GV1" )
local GV2 = GuVec3
GV2.x=GV2.x+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
GV2.y=GV2.y+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
GV2.z=GV2.z+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
GroupUnit:SetState( self, "GT1", GT2 )
GroupUnit:SetState( self, "GV1", GV2 )
local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
local GT = GT2 - GT1
-- Calculate the distance
local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z }
local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z )
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
-- Calculate the group direction vector
local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z }
-- Calculate GH2, GH2 with the same height as CV2.
local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
-- Calculate the angle of GV to the orthonormal plane
local alpha = math.atan2( GV.x, GV.z )
local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
local Inclination = ( Distance + FollowFormation.x ) / 10
if Inclination < -30 then
Inclination = - 30
end
local CVI = {
x = CV2.x + CS * 10 * math.sin(Ca),
y = GH2.y + Inclination, -- + FollowFormation.y,
z = CV2.z + CS * 10 * math.cos(Ca),
}
-- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
-- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
-- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
-- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
-- Now we can calculate the group destination vector GDV.
local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
local GDV_Formation = {
x = GDV.x - GVx,
y = GDV.y,
z = GDV.z - GVz
}
-- Debug smoke.
if self.SmokeDirectionVector == true then
trigger.action.smoke( GDV, trigger.smokeColor.Green )
trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
end
local Time = 120
local Speed = - ( Distance + FollowFormation.x ) / Time
if Distance > -10000 then
Speed = - ( Distance + FollowFormation.x ) / 60
end
if Distance > -2500 then
Speed = - ( Distance + FollowFormation.x ) / 20
end
local GS = Speed + CS
--self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )
-- Now route the escort to the desired point with the desired speed.
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
end
end
end
end
+199 -57
View File
@@ -90,7 +90,8 @@
-- @image Scoring.JPG
--- @type SCORING
-- @field Players A collection of the current players that have joined the game.
-- @field #table Players A collection of the current players that have joined the game.
-- @field Core.Set#SET_SCENERY ScoringScenery
-- @extends Core.Base#BASE
--- SCORING class
@@ -229,7 +230,8 @@ SCORING = {
ClassID = 0,
Players = {},
AutoSave = true,
version = "1.18.4"
version = "1.18.4",
ScoringScenery = nil, -- Core.Set#SET_SCENERY
}
local _SCORINGCoalition = {
@@ -325,6 +327,8 @@ function SCORING:New( GameName, SavePath, AutoSave )
self:OpenCSV( GameName )
end
self:I("SCORING "..tostring(GameName).." started! v"..self.version)
return self
end
@@ -377,7 +381,7 @@ function SCORING:AddUnitScore( ScoreUnit, Score )
return self
end
--- Removes a @{Wrapper.Unit} for additional scoring when the @{Wrapper.Unit} is destroyed.
--- Removes a @{Wrapper.Unit} for scoring when the @{Wrapper.Unit} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Unit#UNIT ScoreUnit The @{Wrapper.Unit} for which the Score needs to be given.
-- @return #SCORING
@@ -394,10 +398,26 @@ end
-- Note that if there was already a @{Wrapper.Static} declared within the scoring with the same name,
-- then the old @{Wrapper.Static} will be replaced with the new @{Wrapper.Static}.
-- @param #SCORING self
-- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @param Wrapper.Static#STATIC ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddStaticScore( ScoreStatic, Score )
return self:AddScoreStatic( ScoreStatic, Score )
end
--- Add a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
-- Note that if there was already a @{Wrapper.Static} declared within the scoring with the same name,
-- then the old @{Wrapper.Static} will be replaced with the new @{Wrapper.Static}.
-- @param #SCORING self
-- @param Wrapper.Static#STATIC ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreStatic( ScoreStatic, Score )
if ScoreStatic == nil then
BASE:E("SCORING.AddStaticScore: Parameter ScoreStatic is nil!")
return self
end
local StaticName = ScoreStatic:GetName()
@@ -406,7 +426,61 @@ function SCORING:AddStaticScore( ScoreStatic, Score )
return self
end
--- Removes a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
--- Add a @{Wrapper.Scenery} for additional scoring when the @{Wrapper.Scenery} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Scenery#SCENERY ScoreScenery The @{Wrapper.Scenery} for which the Score needs to be given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreScenery( ScoreScenery, Score )
if ScoreScenery == nil then
self:E("SCORING.ScoreScenery: Parameter ScoreScenery is nil!")
return self
end
if not self.ScoringScenery then
self.ScoringScenery = SET_SCENERY:New() -- Core.Set#SET_SCENERY
end
local StaticName = ScoreScenery:GetName()
self:T("Scenery name = ".. StaticName)
self.ScoringScenery:AddScenery(ScoreScenery)
return self
end
--- Removes a @{Wrapper.Scenery} for scoring when the @{Wrapper.Scenery} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Scenery#SCENERY ScoreStatic The @{Wrapper.Scenery} for which the Score needs to be given.
-- @return #SCORING
function SCORING:RemoveSceneryScore( ScoreScenery )
local StaticName = ScoreScenery:GetName()
self.ScoringObjects[StaticName] = nil
return self
end
--- Specify a special additional score for a @{Core.Set#SET_SCENERY}.
-- @param #SCORING self
-- @param Core.Set#SET_SCENERY Set The @{Core.Set#SET_SCENERY} for which each @{Wrapper.Scenery} in the SET a Score is given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreSetScenery(Set, Score)
local set = Set.Set
for _,_static in pairs (set) do
if _static ~= nil then
self:AddScoreScenery(_static,Score)
end
end
return self
end
--- Removes a @{Wrapper.Static} for scoring when the @{Wrapper.Static} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @return #SCORING
@@ -461,6 +535,31 @@ function SCORING:AddScoreSetGroup(Set, Score)
return self
end
--- Specify a special additional score for a @{Core.Set#SET_STATIC}.
-- @param #SCORING self
-- @param Core.Set#SET_STATIC Set The @{Core.Set#SET_STATIC} for which each @{Wrapper.Static} in the SET a Score is given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreSetStatic(Set, Score)
local set = Set:GetSetObjects()
for _,_static in pairs (set) do
if _static and _static:IsAlive() then
self:AddStaticScore(_static,Score)
end
end
local function AddScore(static)
self:AddStaticScore(static,Score)
end
function Set:OnAfterAdded(From,Event,To,ObjectName,Object)
AddScore(Object)
end
return self
end
--- Add a @{Core.Zone} to define additional scoring when any object is destroyed in that zone.
-- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
-- This allows for a dynamic destruction zone evolution within your mission.
@@ -480,7 +579,24 @@ function SCORING:AddZoneScore( ScoreZone, Score )
return self
end
--- Remove a @{Core.Zone} for additional scoring.
--- Add a @{Core.Set#SET_ZONE} to define additional scoring when any object is destroyed in that zone.
-- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
-- This allows for a dynamic destruction zone evolution within your mission.
-- @param #SCORING self
-- @param Core.Set#SET_ZONE ScoreZoneSet The @{Core.Set#SET_ZONE} which defines the destruction score perimeters.
-- Note that a zone can be a polygon or a moving zone.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddZoneScoreSet( ScoreZoneSet, Score )
for _,_zone in pairs(ScoreZoneSet.Set or {}) do
self:AddZoneScore(_zone,Score)
end
return self
end
--- Remove a @{Core.Zone} for scoring.
-- The scoring will search if any @{Core.Zone} is added with the given name, and will remove that zone from the scoring.
-- This allows for a dynamic destruction zone evolution within your mission.
-- @param #SCORING self
@@ -958,7 +1074,7 @@ end
-- @param #SCORING self
-- @param Core.Event#EVENTDATA Event
function SCORING:_EventOnHit( Event )
self:F( { Event } )
--self:F( { Event } )
local InitUnit = nil
local InitUNIT = nil
@@ -988,6 +1104,7 @@ function SCORING:_EventOnHit( Event )
local TargetUnitCategory = nil
local TargetUnitType = nil
local TargetIsScenery = false
local TargetSceneryObject = nil
if Event.IniDCSUnit then
@@ -1009,7 +1126,7 @@ function SCORING:_EventOnHit( Event )
InitUnitCategory = _SCORINGCategory[InitCategory]
InitUnitType = InitType
self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType, InitUnitCoalition, InitUnitCategory, InitUnitType } )
--self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType, InitUnitCoalition, InitUnitCategory, InitUnitType } )
end
if Event.TgtDCSUnit then
@@ -1027,18 +1144,23 @@ function SCORING:_EventOnHit( Event )
-- TargetCategory = TargetUnit:getDesc().category
TargetCategory = Event.TgtCategory
TargetType = Event.TgtTypeName
-- Scenery hit
if (not TargetCategory) and TargetUNIT ~= nil and TargetUnit:IsInstanceOf("SCENERY") then
if TargetUNIT ~= nil and TargetUNIT:IsInstanceOf("SCENERY") then
TargetCategory = Unit.Category.STRUCTURE
TargetIsScenery = true
TargetType = "Scenery"
TargetSceneryObject = TargetUNIT
self:T("***** Target is Scenery and TargetUNIT is SCENERY object!")
UTILS.PrintTableToLog(TargetSceneryObject)
end
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
TargetUnitCategory = _SCORINGCategory[TargetCategory]
TargetUnitType = TargetType
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } )
--self:T( { TargetUnitName=TargetUnitName, TargetGroupName=TargetGroupName, TargetPlayerName=TargetPlayerName, TargetCoalition=TargetCoalition, TargetCategory=TargetCategory, TargetType=TargetType, TargetUnitCoalition=TargetUnitCoalition, TargetUnitCategory=TargetUnitCategory, TargetUnitType=TargetUnitType } )
end
if InitPlayerName ~= nil then -- It is a player that is hitting something
@@ -1051,7 +1173,7 @@ function SCORING:_EventOnHit( Event )
self:T( "Hitting Something" )
-- What is he hitting?
if TargetCategory then
if (TargetCategory ~=nil) and (TargetIsScenery == false) then
-- A target got hit, score it.
-- Player contains the score data from self.Players[InitPlayerName]
@@ -1071,7 +1193,7 @@ function SCORING:_EventOnHit( Event )
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
if PlayerHit.UNIT and PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
if PlayerHit.ThreatType == nil or PlayerHit.ThreatType == "" then
@@ -1157,21 +1279,21 @@ function SCORING:_EventOnHit( Event )
-- It is a weapon initiated by a player, that is hitting something
-- This seems to occur only with scenery and static objects.
if Event.WeaponPlayerName ~= nil then
self:_AddPlayerFromUnit( Event.WeaponUNIT )
if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway.
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
self:_AddPlayerFromUnit( TargetUNIT )
end
if Event.WeaponPlayerName ~= nil or TargetIsScenery == true then
local playername = Event.WeaponPlayerName or Event.IniPlayerName or "Ghost"
--self:_AddPlayerFromUnit( Event.WeaponUNIT )
if self.Players[playername] then -- This should normally not happen, but i'll test it anyway.
self:T( "Hitting Scenery" )
self:T( "Hitting Scenery or Static" )
-- What is he hitting?
if TargetCategory then
if Event.TgtObjectCategory then
-- A scenery or static got hit, score it.
-- Player contains the score data from self.Players[WeaponPlayerName]
local Player = self.Players[Event.WeaponPlayerName]
local Player = self.Players[playername]
-- Ensure there is a hit table per TargetCategory and TargetUnitName.
Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {}
@@ -1179,15 +1301,17 @@ function SCORING:_EventOnHit( Event )
-- PlayerHit contains the score counters and data per unit that was hit.
local PlayerHit = Player.Hit[TargetCategory][TargetUnitName]
-- Init player scores
PlayerHit.Score = PlayerHit.Score or 0
PlayerHit.Penalty = PlayerHit.Penalty or 0
PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
if PlayerHit.UNIT and PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
if PlayerHit.ThreatType == nil then
@@ -1195,8 +1319,8 @@ function SCORING:_EventOnHit( Event )
PlayerHit.ThreatType = "Unknown"
end
else
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
PlayerHit.ThreatLevel = PlayerHit.UNIT and PlayerHit.UNIT.ThreatLevel or 1
PlayerHit.ThreatType = PlayerHit.UNIT and PlayerHit.UNIT.ThreatType or "Unknown"
end
-- Only grant hit scores if there was more than one second between the last hit.
@@ -1204,50 +1328,68 @@ function SCORING:_EventOnHit( Event )
PlayerHit.TimeStamp = timer.getTime()
local Score = 0
if InitCoalition then -- A coalition object was hit, probably a static.
if InitCoalition == TargetCoalition then
-- TODO: Penalty according scale
local Penalty = 10
Player.Penalty = Player.Penalty + Penalty --* self.ScaleDestroyPenalty
PlayerHit.Penalty = PlayerHit.Penalty + Penalty --* self.ScaleDestroyPenalty
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
MESSAGE
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. Penalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else
local TgtName = Event.TgtDCSUnit and Event.TgtDCSUnit.getName and Event.TgtDCSUnit:getName() or "Unknown"
--if InitCoalition then -- A coalition object was hit, probably a static.
if TargetIsScenery == true and self.ScoringScenery:IsInSet(TargetSceneryObject) then
Player.Score = Player.Score + self.ScoreIncrementOnHit
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. playername .. "' hit scenery target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end
else -- A scenery object was hit.
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.",
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( playername, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
elseif TargetIsScenery == false and Event.TgtObjectCategory == Object.Category.STATIC and self.ScoringObjects[TgtName] then
Player.Score = Player.Score + self.ScoreIncrementOnHit
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. playername .. "' hit static target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType )
end
self:ScoreCSV( playername, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else
self:E("Hit unregistered scenery or static object - NO target! ("..TgtName..")")
end
--end
end
end
end
-- Check if there are Zones where the destruction happened.
for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do
self:F( { ScoringZone = ScoreZoneData } )
local hit=Event.TgtUnit
local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE
local Score = ScoreZoneData.Score
if TargetUNIT and ScoreZone:IsVec2InZone( TargetUNIT:GetVec2() ) then
-- A scenery or static got hit, score it.
-- Player contains the score data from self.Players[WeaponPlayerName]
local PlayerName = Event.IniPlayerName or "Ghost"
local Player = self.Players[PlayerName]
Player.Score = Player.Score + Score
Player.Score = Player.Score + self.ScoreIncrementOnHit
MESSAGE:NewType( self.DisplayMessagePrefix .. "hit in zone '" .. ScoreZone:GetName() .. "'." ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, "", "HIT_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Zone", TargetUnitType )
end
end
end
end
end
--- Track DEAD or CRASH events for the scoring.
-- @param #SCORING self
-- @param Core.Event#EVENTDATA Event
@@ -1305,7 +1447,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
local Destroyed = false
-- What is the player destroying?
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and TargetUnit and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
@@ -1132,7 +1132,7 @@
-- 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.
-- In particular, we use the @{Functional.Formation#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
@@ -1175,7 +1175,7 @@
--
-- -- 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", "Escort Carrier.")
-- CarrierFormationLeft = FORMATION:New(Mother, groupset, "Left Formation with Carrier")
--
-- -- Formation parameters.
-- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50)
@@ -1190,7 +1190,7 @@
--
-- -- 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", "Escort Carrier.")
-- CarrierFormationRight = FORMATION:New(Mother, groupset, "Right Formation with Carrier")
--
-- -- Formation parameters.
-- CarrierFormationRight:FormationRightWing(200 ,50, 0, 0, 500, 50)
@@ -1208,7 +1208,7 @@
-- group:StartUncontrolled()
--
-- -- Define AI Formation object.
-- CarrierFormationHelo = AI_FORMATION:New(Mother, groupset, "Helo Formation with Carrier", "Fly Formation.")
-- CarrierFormationHelo = FORMATION:New(Mother, groupset, "Helo Formation with Carrier")
--
-- -- Formation parameters.
-- CarrierFormationHelo:FormationCenterWing(-150, 50, 20, 50, 100, 50)
+1
View File
@@ -85,6 +85,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ZoneGoalCargo.lua'
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Tiresias.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Stratego.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ClientWatch.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Formation.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Airboss.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' )
@@ -18,7 +18,7 @@
--
-- ===
-- @module Navigation.Beacons
-- @image NAVIGATION_Beacons.png
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -43,6 +43,7 @@
--
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `beacons.lua` that can be
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data. `Package` als needs to be available.
--
-- # Basic Setup
--
+1 -1
View File
@@ -17,7 +17,7 @@
--
-- ===
-- @module Navigation.Point
-- @image NAVIGATION_Point.png
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -18,7 +18,7 @@
--
-- ===
-- @module Navigation.Radios
-- @image NAVIGATION_Radios.png
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -43,6 +43,7 @@
--
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `radio.lua` that can be
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data.
--
-- # Basic Setup
--
+2 -1
View File
@@ -18,7 +18,7 @@
--
-- ===
-- @module Navigation.Towns
-- @image NAVIGATION_Towns.png
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -43,6 +43,7 @@
--
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `towns.lua` that can be
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data.
--
-- # Basic Setup
--
+11 -9
View File
@@ -45,7 +45,7 @@
-- **Supported Aircraft:**
--
-- * [F/A-18C Hornet Lot 20](https://forums.eagle.ru/forumdisplay.php?f=557) (Player & AI)
-- * [F-14A/B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
-- * [F-14A/B/A Early Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
-- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI)
-- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI)
-- * [T-45C Goshawk](https://forum.dcs.world/topic/203816-vnao-t-45-goshawk/) (VNAO mod) (Player & AI)
@@ -63,7 +63,7 @@
-- no other fixed wing aircraft (human or AI controlled) are supposed to land on these ships. Currently only Case I is supported. Case II/III take slightly different steps from the CVN carrier.
-- However, if no offset is used for the holding radial this provides a very close representation of the V/STOL Case III, allowing for an approach to over the deck and a vertical landing.
--
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
-- Heatblur's mighty F-14A/B/A Early Tomcat has been added as well.
--
-- The [DCS Supercarriers](https://www.digitalcombatsimulator.com/de/shop/modules/supercarrier/) are also supported.
--
@@ -1276,6 +1276,7 @@ AIRBOSS = {
-- @field #string A4EC A-4E Community mod.
-- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics.
-- @field #string F14A F-14A by Heatblur.
-- @field #string F14A_Early F-14A-135-GR-Early by Heatblur.
-- @field #string F14B F-14B by Heatblur.
-- @field #string F14A_AI F-14A Tomcat (AI).
-- @field #string FA18C F/A-18C Hornet (AI).
@@ -1294,6 +1295,7 @@ AIRBOSS.AircraftCarrier={
HORNET="FA-18C_hornet",
A4EC="A-4E-C",
F14A="F-14A-135-GR",
F14A_Early="F-14A-135-GR-Early",
F14B="F-14B",
F14A_AI="F-14A",
FA18C="F/A-18C",
@@ -5473,7 +5475,7 @@ function AIRBOSS:_GetAircraftAoA( playerData )
local goshawk = playerData.actype == AIRBOSS.AircraftCarrier.T45C
local skyhawk = playerData.actype == AIRBOSS.AircraftCarrier.A4EC
local harrier = playerData.actype == AIRBOSS.AircraftCarrier.AV8B
local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B
local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B or playerData.actype == AIRBOSS.AircraftCarrier.F14A_Early
local corsair = playerData.actype == AIRBOSS.AircraftCarrier.CORSAIR or playerData.actype == AIRBOSS.AircraftCarrier.CORSAIR_CW
-- Table with AoA values.
@@ -5552,7 +5554,7 @@ function AIRBOSS:_AoAUnit2Deg( playerData, aoaunits )
local degrees = aoaunits
-- Check aircraft type of player.
if playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B then
if playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B or playerData.actype == AIRBOSS.AircraftCarrier.F14A_Early then
-------------
-- F-14A/B --
@@ -5595,7 +5597,7 @@ function AIRBOSS:_AoADeg2Units( playerData, degrees )
local aoaunits = degrees
-- Check aircraft type of player.
if playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B then
if playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B or playerData.actype == AIRBOSS.AircraftCarrier.F14A_Early then
-------------
-- F-14A/B --
@@ -11593,7 +11595,7 @@ function AIRBOSS:_AttitudeMonitor( playerData )
local unitClient = Unit.getByName(unit:GetName()) -- VNAO Edit - Added
local hornet = playerData.actype == AIRBOSS.AircraftCarrier.HORNET -- VNAO Edit - Added
local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B -- VNAO Edit - Added
local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B or playerData.actype == AIRBOSS.AircraftCarrier.F14A_Early -- VNAO Edit - Added
if hornet then -- VNAO Edit - Added
local nozzlePosL = 0 -- VNAO Edit - Added
@@ -11826,7 +11828,7 @@ function AIRBOSS:_NozzleArgumentLeft( unit ) -- VNAO Edit - Added
else -- VNAO Edit - Added
nozzlePosL = 0 -- VNAO Edit - Added
end -- VNAO Edit - Added
elseif typeName == "F-14A-135-GR" or typeName == "F-14B" then -- VNAO Edit - Added
elseif typeName == "F-14A-135-GR" or typeName == "F-14B" or typeName == "F-14A-135-GR-Early" then -- VNAO Edit - Added
nozzlePosL = unitClient:getDrawArgumentValue(434) -- VNAO Edit - Added
end -- VNAO Edit - Added
@@ -11851,7 +11853,7 @@ function AIRBOSS:_NozzleArgumentRight( unit ) -- VNAO Edit - Added
else -- VNAO Edit - Added
nozzlePosR = 0 -- VNAO Edit - Added
end -- VNAO Edit - Added
elseif typeName == "F-14A-135-GR" or typeName == "F-14B" then -- VNAO Edit - Added
elseif typeName == "F-14A-135-GR" or typeName == "F-14B" or typeName == "F-14A-135-GR-Early" then -- VNAO Edit - Added
nozzlePosR = unitClient:getDrawArgumentValue(433) -- VNAO Edit - Added
end -- VNAO Edit - Added
return nozzlePosR -- VNAO Edit - Added
@@ -15066,7 +15068,7 @@ function AIRBOSS:_GetACNickname( actype )
nickname = "Hawkeye"
elseif actype == AIRBOSS.AircraftCarrier.C2A then
nickname = "Greyhound"
elseif actype == AIRBOSS.AircraftCarrier.F14A_AI or actype == AIRBOSS.AircraftCarrier.F14A or actype == AIRBOSS.AircraftCarrier.F14B then
elseif actype == AIRBOSS.AircraftCarrier.F14A_AI or actype == AIRBOSS.AircraftCarrier.F14A or actype == AIRBOSS.AircraftCarrier.F14B or actype == AIRBOSS.AircraftCarrier.F14A_Early then
nickname = "Tomcat"
elseif actype == AIRBOSS.AircraftCarrier.FA18C or actype == AIRBOSS.AircraftCarrier.HORNET then
nickname = "Hornet"
+1 -1
View File
@@ -2037,7 +2037,7 @@ function AUFTRAG:NewRESCUEHELO(Carrier)
-- Mission options:
mission.missionTask=ENUMS.MissionTask.NOTHING
mission.missionFraction=0.5
mission.missionFraction=0.9
mission.optionROE=ENUMS.ROE.WeaponHold
mission.optionROT=ENUMS.ROT.NoReaction
+38 -11
View File
@@ -4308,7 +4308,27 @@ function OPSGROUP:_UpdateTask(Task, Mission)
Mission=Mission or self:GetMissionByTaskID(self.taskcurrent)
if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then
if Mission.type == AUFTRAG.Type.RESCUEHELO then
self:T("**********")
self:T("** RESCUEHELO USED")
self:T("**********")
local param=Task.dcstask.params
local followUnit=UNIT:FindByName(param.unitname)
local helogroupname = self:GetGroup():GetName()
Task.formation = RESCUEHELO:New(followUnit,helogroupname)
Task.formation:SetRespawnOnOff(false)
Task.formation.respawninair=false
Task.formation:SetTakeoffCold()
Task.formation:SetHomeBase(followUnit)
Task.formation.helo = self:GetGroup()
-- Start formation FSM.
Task.formation:Start()
if self:IsFlightgroup() then
self:SetDespawnAfterLanding()
end
else
-- Set of group(s) to follow Mother.
local followSet=SET_GROUP:New():AddGroup(self.group)
@@ -4317,7 +4337,7 @@ function OPSGROUP:_UpdateTask(Task, Mission)
local followUnit=UNIT:FindByName(param.unitname)
-- Define AI Formation object.
Task.formation=AI_FORMATION:New(followUnit, followSet, AUFTRAG.SpecialTask.FORMATION, "Follow X at given parameters.")
Task.formation=FORMATION:New(followUnit, followSet, AUFTRAG.SpecialTask.FORMATION)
-- Formation parameters.
Task.formation:FormationCenterWing(-param.offsetX, 50, math.abs(param.altitude), 50, param.offsetZ, 50)
@@ -4326,11 +4346,13 @@ function OPSGROUP:_UpdateTask(Task, Mission)
Task.formation:SetFollowTimeInterval(param.dtFollow)
-- Formation mode.
Task.formation:SetFlightModeFormation(self.group)
--Task.formation:SetFlightModeFormation(self.group)
-- Start formation FSM.
Task.formation:Start()
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
---
@@ -8026,11 +8048,16 @@ function OPSGROUP:onafterDead(From, Event, To)
-- Get asset.
local asset=self.legion:GetAssetByName(self.groupname)
if asset then
-- Get request.
local request=self.legion:GetRequestByID(asset.rid)
-- Trigger asset dead event.
self.legion:AssetDead(asset, request)
end
end
-- Stop in 5 sec to give possible respawn attempts a chance.
@@ -12723,7 +12750,7 @@ end
-- @return #OPSGROUP self
function OPSGROUP:SetDefaultCallsign(CallsignName, CallsignNumber)
self:T(self.lid..string.format("Setting Default callsing %s-%s", tostring(CallsignName), tostring(CallsignNumber)))
self:T(self.lid..string.format("Setting Default callsign %s-%s", tostring(CallsignName), tostring(CallsignNumber)))
self.callsignDefault={} --#OPSGROUP.Callsign
self.callsignDefault.NumberSquad=CallsignName
@@ -13482,8 +13509,8 @@ function OPSGROUP:GetAmmoUnit(unit, display)
if ammotable then
local weapons=#ammotable
--self:I(ammotable)
--UTILS.PrintTableToLog(ammotable)
--self:I(ammotable)
--UTILS.PrintTableToLog(ammotable)
-- Loop over all weapons.
for w=1,weapons do
@@ -13491,9 +13518,9 @@ function OPSGROUP:GetAmmoUnit(unit, display)
-- Number of current weapon.
local Nammo=ammotable[w]["count"]
-- Range in meters. Seems only to exist for missiles (not shells).
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
-- Range in meters. Seems only to exist for missiles (not shells).
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
-- Type name of current weapon.
local Tammo=ammotable[w]["desc"]["typeName"]
@@ -13515,7 +13542,7 @@ function OPSGROUP:GetAmmoUnit(unit, display)
-- Add up all shells.
nshells=nshells+Nammo
-- Add small and large caliber shells for guns and cannons
if ammotable[w]["desc"]["warhead"] and ammotable[w]["desc"]["warhead"]["caliber"] then
local caliber=ammotable[w]["desc"]["warhead"]["caliber"]
+27 -18
View File
@@ -20,7 +20,6 @@
-- ===
--
-- ### Author: **funkyfranky**
-- ### Contributions: Flightcontrol (@{AI.AI_Formation} class being used here)
--
-- @module Ops.RescueHelo
-- @image Ops_RescueHelo.png
@@ -37,7 +36,7 @@
-- @field #number takeoff Takeoff type.
-- @field Wrapper.Airbase#AIRBASE airbase The airbase object acting as home base of the helo.
-- @field Core.Set#SET_GROUP followset Follow group set.
-- @field AI.AI_Formation#AI_FORMATION formation AI_FORMATION object.
-- @field Functional.Formation#FORMATION formation FORMATION object.
-- @field #number lowfuel Low fuel threshold of helo in percent.
-- @field #number altitude Altitude of helo in meters.
-- @field #number offsetX Offset in meters to carrier in longitudinal direction.
@@ -235,7 +234,7 @@ _RESCUEHELOID=0
--- Class version.
-- @field #string version
RESCUEHELO.version="1.1.0"
RESCUEHELO.version="1.2.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -248,6 +247,7 @@ RESCUEHELO.version="1.1.0"
-- DONE: Possibility to add already present/spawned aircraft, e.g. for warehouse.
-- DONE: Add rescue event when aircraft crashes.
-- DONE: Make offset input parameter.
-- DONE: Make useable for AUFTRAG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -337,6 +337,9 @@ function RESCUEHELO:New(carrierunit, helogroupname)
self:AddTransition("*", "Status", "*")
self:AddTransition("*", "Stop", "Stopped")
self:I(self.lid.."Started.")
--- Triggers the FSM event "Start" that starts the rescue helo. Initializes parameters and starts event handlers.
-- @function [parent=#RESCUEHELO] Start
@@ -871,15 +874,25 @@ function RESCUEHELO:onafterStart(From, Event, To)
-- Delay before formation is started.
local delay=120
-- Spawn helo. We need to introduce an alias in case this class is used twice. This would confuse the spawn routine.
local Spawn=SPAWN:NewWithAlias(self.helogroupname, self.alias)
-- Set modex for spawn.
Spawn:InitModex(self.modex)
local UsesAliveGroup=false
local Spawn = GROUP:FindByName(self.helogroupname)
if Spawn and Spawn:IsAlive() then
self.helo=Spawn
UsesAliveGroup = true
delay = 1
else
-- Spawn helo. We need to introduce an alias in case this class is used twice. This would confuse the spawn routine.
local Spawn=SPAWN:NewWithAlias(self.helogroupname, self.alias)
-- Set modex for spawn.
Spawn:InitModex(self.modex)
end
-- Spawn in air or at airbase.
if self.takeoff==SPAWN.Takeoff.Air then
if UsesAliveGroup==false and self.takeoff==SPAWN.Takeoff.Air then
-- Carrier heading
local hdg=self.carrier:GetHeading()
@@ -899,7 +912,7 @@ function RESCUEHELO:onafterStart(From, Event, To)
-- Start formation in 1 seconds
delay=1
else
elseif UsesAliveGroup==false and self.uncontrolledac then
-- Check if an uncontrolled helo group was requested.
if self.uncontrolledac then
@@ -919,9 +932,9 @@ function RESCUEHELO:onafterStart(From, Event, To)
-- No group of that name!
self:E(string.format("ERROR: No uncontrolled (alive) rescue helo group with name %s could be found!", self.helogroupname))
return
end
end
else
elseif UsesAliveGroup==false then
-- Spawn at airbase.
self.helo=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, AIRBASE.TerminalType.HelicopterUsable)
@@ -947,17 +960,13 @@ function RESCUEHELO:onafterStart(From, Event, To)
self.HeloFuel0=self.helo:GetFuel()
-- Define AI Formation object.
self.formation=AI_FORMATION:New(self.carrier, self.followset, "Helo Formation with Carrier", "Follow Carrier at given parameters.")
self.formation=FORMATION:New(self.carrier, self.followset, "Helo Formation with Carrier")
-- Formation parameters.
self.formation:FormationCenterWing(-self.offsetX, 50, math.abs(self.altitude), 50, self.offsetZ, 50)
-- Set follow time interval.
self.formation:SetFollowTimeInterval(self.dtFollow)
-- Formation mode.
self.formation:SetFlightModeFormation(self.helo)
-- Start formation FSM.
self.formation:__Start(delay)
+113
View File
@@ -5236,3 +5236,116 @@ function UTILS.ValidateAndRepositionStatic(Country, Category, Type, Position, Sh
return nil
end
---
-- This function can be used to scan all airdromes of a map and write the Moose enums to a file.
function UTILS.CreateAirbaseEnum()
-- Save file function
local function _savefile( filename, data )
local file=lfs.writedir() .. filename
local f = io.open(file , "wb")
if f then
f:write( data )
f:close()
env.info(string.format("Saving to file %s", tostring(file) ))
else
env.info(string.format("ERROR: Could not save results to file %s", tostring(file) ))
end
end
-- Get all airbases
local airbases=world.getAirbases()
-- Get map name
local mapname=env.mission.theatre
-- Put airbases in a table
local myab={}
for i,_airbase in pairs(airbases) do
local airbase=_airbase --DCS#Airbase
local cat=airbase:getDesc().category
if cat==Airbase.Category.AIRDROME then
-- Get airbase name
local name=airbase:getName()
-- Key is the name with illigal characters replaced
local key=name
-- Germany map specifics
if name=="Airracing Lubeck" then
key="Airracing_Luebeck"
elseif name=="Bad Durkheim" then
key="Bad_Duerkheim"
elseif name=="Buchel" then
key="Buechel"
elseif name=="Buckeburg" then
key="Bueckeburg"
elseif name=="Dusseldorf" then
key="Duesseldorf"
elseif name=="Gutersloh" then
key="Guetersloh"
elseif name=="Kothen" then
key="Koethen"
elseif name=="Larz" then
key="Laerz"
elseif name=="Lubeck" then
key="Luebeck"
elseif name=="Luneburg" then
key="Lueneburg"
elseif name=="Norvenich" then
key="Noervenich"
elseif name=="Ober-Morlen" then
key="Ober_Moerlen"
elseif name=="Peenemunde" then
key="Peenemuende"
elseif name=="Pottschutthohe" then
key="Pottschutthoehe"
elseif name=="Schonefeld" then
key="Schoenefeld"
elseif name=="Weser Wumme" then
key="Weser_Wuemme"
elseif name=="Zollschen" then
key="Zoellschen"
elseif name=="Zweibrucken" then
key="Zweibruecken"
end
-- Replace blanks, hyphens by underscores
key=key:gsub(" ", "_")
key=key:gsub("-", "_")
key=key:gsub("'", "_")
key=UTILS.ReplaceIllegalCharacters(key, "_")
local entry={}
entry.key=key
entry.name=name
table.insert(myab, entry)
end
end
-- Sort by name
table.sort(myab, function(a,b) return a.name < b.name end)
local text=string.format("\n--- Airbases of the %s map", mapname)
text=text.."\n--"
for _,ab in pairs(myab) do
text=text..string.format("\n-- * `AIRBASE.%s.%s` %s", mapname, ab.key, ab.name)
end
text=text.."\n--"
text=text..string.format("\n-- @field %s", mapname)
text=text..string.format("\nAIRBASE.%s = {", mapname)
for _,ab in pairs(myab) do
text=text..string.format('\n\t["%s"] = "%s",', ab.key, ab.name)
end
text=text.."\n}"
_savefile(string.format("%s-enums.txt", env.mission.theatre), text)
--env.info(text)
end
File diff suppressed because it is too large Load Diff
@@ -409,7 +409,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
local Controller = self:_GetController()
-- self:I( "Before SetTask" )
Controller:setTask( DCSTask )
-- AI_FORMATION class (used by RESCUEHELO) calls SetTask twice per second! hence spamming the DCS log file ==> setting this to trace.
-- FORMATION class (used by RESCUEHELO) calls SetTask twice per second! hence spamming the DCS log file ==> setting this to trace.
self:T( { ControllableName = self:GetName(), DCSTask = DCSTask } )
else
BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } )
+166 -27
View File
@@ -18,7 +18,13 @@
-- @field #string SceneryName Name of the scenery object.
-- @field DCS#Object SceneryObject DCS scenery object.
-- @field #number Life0 Initial life points.
-- @field #table Properties
-- @field #table Properties.
-- @field #number ID.
-- @field Core.Zone#ZONE_POLYGON SceneryZone.
-- @field Core.Point#COORDINATE Coordinate.
-- @field DCS#Vec2 Vec2.
-- @field DCS#Vec3 Vec3.
-- @field Core.Vector#VECTOR Vector.
-- @extends Wrapper.Positionable#POSITIONABLE
@@ -35,27 +41,79 @@ SCENERY = {
ClassName = "SCENERY",
}
_SCENERY = {}
--- Register scenery object as POSITIONABLE.
--@param #SCENERY self
--@param #string SceneryName Scenery name.
--@param DCS#Object SceneryObject DCS scenery object.
--@param Core.Zone#ZONE_POLYGON SceneryZone (optional) The zone object.
--@return #SCENERY Scenery object.
function SCENERY:Register( SceneryName, SceneryObject )
function SCENERY:Register( SceneryName, SceneryObject, SceneryZone )
local ID = (SceneryObject and SceneryObject.getID) and SceneryObject:getID() or SceneryName
if _SCENERY[ID] and _SCENERY[ID].SceneryObject == nil then
_SCENERY[ID].SceneryObject=SceneryObject
SCENERY._UpdateFromDCSObject(_SCENERY[ID])
end
if _SCENERY[ID] then return _SCENERY[ID] end
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
self.SceneryName = tostring(SceneryName)
self.ID = ID
self.SceneryObject = SceneryObject
self.SceneryZone = SceneryZone
if SceneryZone then
self.Vec3 = SceneryZone:GetVec3()
self.Vec2 = SceneryZone:GetVec2()
self.Vector = (self.Vec3 and VECTOR) and VECTOR:NewFromVec(self.Vec3) or nil
end
if SceneryObject then
local vec3 = SceneryObject:getPoint()
self.Vec3 = { x = vec3.x, y = vec3.y, z = vec3.z }
self.Vec2 = { x = vec3.x, y = vec3.z }
self.Vector = (self.Vec3 and VECTOR) and VECTOR:NewFromVec(self.Vec3) or nil
end
if self.SceneryObject and self.SceneryObject.getLife then -- fix some objects do not have all functions
self.Life0 = self.SceneryObject:getLife() or 0
self.Life0 = self.SceneryObject:getLife() or 1
else
self.Life0 = 0
self.Life0 = 1
end
self.Properties = {}
_SCENERY[self.ID] = self
return self
end
--- [INTERNAL] Update data
-- @param Wrapper.Scenery#SCENERY Scenery The object to update.
function SCENERY._UpdateFromDCSObject(Scenery)
env.info("APPLE _UpdateFromDCSObject "..tostring(Scenery.SceneryName))
local self=Scenery
if self.Vec2 == nil and self.SceneryObject ~= nil then
self.Vec3 = self.SceneryObject:getPoint()
if self.Vec3 then
self.Vec2 = {x=self.Vec3.x,y=self.Vec3.z}
self.Vector = VECTOR:NewFromVec(self.Vec3)
end
end
if not self.Life0 or self.Life0 == 1 then
if self.SceneryObject and self.SceneryObject.getLife() then
self.Life = self.SceneryObject:getLife() or 1
self.Life0 = self.Life
end
end
return self
end
@@ -99,6 +157,39 @@ function SCENERY:GetName()
return self.SceneryName
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return Core.Point#COORDINATE Coordinate
function SCENERY:GetCoordinate()
if self.Coordinate then
return self.Coordinate
elseif self.Vec3 then
self.Coordinate = COORDINATE:NewFromVec3(self.Vec3):SetAlt()
end
return self.Coordinate
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return DCS#Vec3 Vec3
function SCENERY:GetVec3()
return self.Vec3
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return DCS#Vec2 Vec2
function SCENERY:GetVec2()
return self.Vec2
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return Core.Vector#VECTOR Vector
function SCENERY:GetVector()
return self.Vector
end
--- Obtain DCS Object from the SCENERY Object.
--@param #SCENERY self
--@return DCS#Object DCS scenery object.
@@ -106,6 +197,13 @@ function SCENERY:GetDCSObject()
return self.SceneryObject
end
--- Obtain object ID.
--@param #SCENERY self
--@return #string Name
function SCENERY:GetID()
return self.ID
end
--- Get current life points from the SCENERY Object. Note - Some scenery objects always have 0 life points.
-- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the life0 value to 120%
-- of the last life value if life exceeds life0 (initial life) at any point. Thus will will get a smooth percentage decrease, if you use this e.g. as success
@@ -113,12 +211,14 @@ end
--@param #SCENERY self
--@return #number life
function SCENERY:GetLife()
local life = 0
local life = 1
if self.SceneryObject and self.SceneryObject.getLife then
life = self.SceneryObject:getLife()
if life > self.Life0 then
self.Life0 = math.floor(life * 1.2)
end
elseif self.Life then
life = self.Life
end
return life
end
@@ -176,12 +276,19 @@ end
--- Find a SCENERY object from its name or id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
-- to find the correct object.
--@param #SCENERY self
--@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449'
--@param Core.Point#COORDINATE Coordinate Where to find the scenery object
--@param #number Radius (optional) Search radius around coordinate, defaults to 100
--@return #SCENERY Scenery Object or `nil` if it cannot be found
function SCENERY:FindByName(Name, Coordinate, Radius, Role)
--@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449'.
--@param Core.Point#COORDINATE Coordinate Where to find the scenery object.
--@param #number Radius (optional) Search radius around coordinate, defaults to 100.
--@param #string Role (optional) The role if set on the zone object.
--@param Core.Zone#ZONE_POLYGON Zone (optional) The Zone where the scenery is located.
--@return #SCENERY Scenery Object or `nil` if it cannot be found.
function SCENERY:FindByName(Name, Coordinate, Radius, Role, Zone)
--BASE:I("Coordinate x = "..Coordinate.x .. " y = "..Coordinate.y.." z = "..Coordinate.z)
local findme = self:_FindByName(Name)
if findme then return findme end
local radius = Radius or 100
local name = Name or "unknown"
local scenery = nil
@@ -193,12 +300,13 @@ function SCENERY:FindByName(Name, Coordinate, Radius, Role)
local function SceneryScan(scoordinate, sradius, sname)
if scoordinate ~= nil then
local Vec2 = scoordinate:GetVec2()
local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius,true)
local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius)
scanzone:Scan({Object.Category.SCENERY})
local scanned = scanzone:GetScannedSceneryObjects()
local rscenery = nil -- Wrapper.Scenery#SCENERY
for _,_scenery in pairs(scanned) do
local scenery = _scenery -- Wrapper.Scenery#SCENERY
--BASE:I({tostring(scenery.SceneryName),tostring(sname)})
if tostring(scenery.SceneryName) == tostring(sname) then
rscenery = scenery
if Role then rscenery:SetProperty("ROLE",Role) end
@@ -211,13 +319,36 @@ function SCENERY:FindByName(Name, Coordinate, Radius, Role)
end
if Coordinate then
--BASE:I("Coordinate Scenery Scan")
scenery = SceneryScan(Coordinate, radius, name)
end
if not scenery then scenery = SCENERY:Register(Name,nil,Zone) end
return scenery
end
--- Find a SCENERY object that was previously registered(!) by it's ID.
-- @param #SCENERY self
-- @param #number ID
-- @return Wrapper.Scenery#SCENERY Scenery or nil if it could not be found
function SCENERY:FindByID(ID)
return _SCENERY[ID]
end
--- Find a SCENERY object that was previously registered(!) by it's name.
-- @param #SCENERY self
-- @param #string Name
-- @return Wrapper.Scenery#SCENERY Scenery or nil if it could not be found
function SCENERY:_FindByName(Name)
for _id,_object in pairs(_SCENERY) do
if _object and _object.GetName and _object:GetName() then
local name = _object:GetName()
if Name == name then return _object end
end
end
return nil
end
--- Find a SCENERY object from its name or id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
-- to find the correct object.
--@param #SCENERY self
@@ -231,8 +362,8 @@ function SCENERY:FindByNameInZone(Name, Zone, Radius)
if type(Zone) == "string" then
Zone = ZONE:FindByName(Zone)
end
local coordinate = Zone:GetCoordinate()
return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE"))
local coordinate = Zone:GetCoordinate():SetAlt()
return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE"),Zone)
end
--- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
@@ -241,14 +372,18 @@ end
--@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object.
--@return #SCENERY First found Scenery Object or `nil` if it cannot be found
function SCENERY:FindByZoneName( ZoneName )
--BASE:I(ZoneName)
local zone = ZoneName -- Core.Zone#ZONE_BASE
if type(ZoneName) == "string" then
zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_POLYGON
end
local _id = zone:GetProperty('OBJECT ID')
--local properties = zone:GetAllProperties() or {}
--BASE:I(string.format("Object ID is: %s",_id or "none"))
--BASE:T("Object ID ".._id)
--BASE:I("Object ID ".._id)
if not _id then
-- this zone has no object ID
BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName))
@@ -257,23 +392,27 @@ function SCENERY:FindByZoneName( ZoneName )
local scanned = zone:GetScannedSceneryObjects()
for _,_scenery in (scanned) do
local scenery = _scenery -- Wrapper.Scenery#SCENERY
if scenery:IsAlive() then
--if scenery:IsAlive() then
local role = zone:GetProperty("ROLE")
if role then scenery:SetProperty("ROLE",role) end
return scenery
end
--end
end
return nil
else
return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
local coordinate = zone:GetCoordinate()
coordinate:SetAlt()
return self:FindByName(_id, coordinate,nil,zone:GetProperty("ROLE"),zone)
end
else
return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
local coordinate = zone:GetCoordinate()
coordinate:SetAlt()
return self:FindByName(_id, coordinate,nil,zone:GetProperty("ROLE"),zone)
end
end
--- Scan and find all SCENERY objects from a zone by zone-name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
-- to find the correct object.
-- to find the correct object. Might return nil!
--@param #SCENERY self
--@param #string ZoneName The name of the zone, can be handed as ZONE_RADIUS or ZONE_POLYGON object
--@return #table of SCENERY Objects, or `nil` if nothing found
@@ -295,7 +434,7 @@ function SCENERY:FindAllByZoneName( ZoneName )
return nil
end
else
local obj = self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
local obj = self:FindByName(_id, zone:GetCoordinate():SetAlt(),nil,zone:GetProperty("ROLE"), zone)
if obj then
return {obj}
else