Compare commits

..

28 Commits

Author SHA1 Message Date
Frank
bf61f22dd1 Fixed UnHandleEvent for UNIT
#1365
2020-11-15 23:00:03 +01:00
Frank
5e8a36802c Github issues
#1360
#1361
2020-10-31 21:21:25 +01:00
Frank
b34b537a26 Update AI_Formation.lua
- Issue #1358
2020-10-28 09:46:03 +01:00
Frank
812fb99e66 Small Updates
AIRBOSS
- Added USS Harry S. Truman (CVN-75) [Super Carrier Module]
- Fixed little bug in stop time

RADIOQUEUE
- Allowing GROUND units as relay. Only ships cannot be used.
2020-10-26 16:16:04 +01:00
Frank
9cb3111dd5 Github issues addressed
- Fixes #1342
- Fixes #1333
- Fixes #1331
2020-10-26 09:13:49 +01:00
Frank
1312555690 Github Issues Addressed
**GROUP**
#1354
**ARTY v1.1.8**
#1356
#1357
2020-10-20 23:26:32 +02:00
Frank
aa4f8452fa Update Airbase.lua
- Fixed a bug inthe `AIRBASE:GetRunwayData()` function.
2020-10-03 23:11:05 +02:00
Frank
e2c1097ec5 Update Airboss.lua
- Fix for DCS bug that skill is not set in env.mission. Should not happen in current DCS version anyway.
2020-10-03 21:41:35 +02:00
Frank
59f795fc17 Merge pull request #1349 from FlightControl-Master/FF/MasterDevel
RANGE, TIMER & Minor
2020-09-02 00:08:36 +02:00
Frank
26a935c29c RANGE etc
RANGE v2.2.3
- Added relay unit option.

TIMER
- Fixed bug.

Minor other stuff
2020-09-01 17:30:26 +02:00
Frank
848336f8ac Merge pull request #1348 from FlightControl-Master/FF/MasterDevel
Updates, fixes and new classes
2020-08-29 22:15:14 +02:00
Frank
8e6e9cbb4d Updated Moose Modules.lua
- Added new class files Profiler, Timer, Marker
2020-08-29 22:10:45 +02:00
Frank
15cb9bec40 Updates, new classes and fixes
**AI_FORMATION**
- Performance improvents. This also affects the **RESCUEHELO** class.

**EVENT**
- Added events when object is of category BASE.

**FSM**
- Removed a few tracing calls.
- Updated docs.

**POINT**
- Added Update Vec functions to COORDINATE.
- Added *overwrite* option to COORDINATE:Translate() function.
- Removed second COORDINATE:Translate() function.
- Optimized COORDINATE:GetClosestAirbase() function.
- Added *Offset* parameter to COORDINATE:IsLOS() function.

**RADIO**
- Updated BEACON type and system enums.

**RADIOQUEUE**
- Use Vec3 instead of COORDINATE. Performance improvement.

**SET**
- Added some functions.

**TIMER**
- Added new class. Little sister of SCHEDULER class.

**DCS**
- Minor changes regarding docs.

**ATIS**
- Added "Miles.ogg", "StatuteMiles.ogg", "Zulu.ogg".

**ENUMS**
- Added ENUMS.Formation.Vehicle
- Added ENUMS.AlarmState

** PROFILER**
- Added new lua profiler.

**UTILS**
- Minor changes and additions.

**AIRBASE**
- Improved Registration.
- Improved Parking spot handling.
- Aded :IsAirdrome(), IsHelipad(), IsShip() functions.
- Improved :GetRunwayData() for Syria airports.

**CONTROLLABLE**
- Fixed bug in :CommandSetFrequency() fuction (Hz vs. MHz).
- Updated/fixed :TaskFAC_AttackGroup() function.

**GROUP**
- Added :GetThreatLevel() function.
- Added :IsInZone() function to check if any unit is in the zone.

**MARKER**
- Added new class handling F10 markers using FSM.
2020-08-29 21:55:59 +02:00
Frank
38f5fd8249 SET_STATIC
- Fixed #1345
2020-08-19 23:10:20 +02:00
Frank
6a04f83280 Syria
ATIS
- Added runway magnetic to true conversion. Channel map -10°, Syria +5°.
- ICAOP phraseology The Channel and Syria true.

UTILS
- Added DCSMAP.Syria="Syria"
- Magnetic declination for Syria set to 5°
- GMT to local time for Syria is GMT+3.

AIRBASE
- Added AIRBASE.Syria enums.
2020-08-19 23:04:38 +02:00
Frank
ec4d12a0d5 Merge pull request #1338 from RISCfuture/improve-atis
Modify ATIS format to more closely match real world
2020-08-19 22:28:31 +02:00
Frank
999aba5d77 Update README.md 2020-07-19 21:15:18 +02:00
Tim Morgan
652f29b782 Use SM for visibility, not NM 2020-07-11 13:15:50 -07:00
Tim Morgan
0c6aae45c1 Do not report visibilities > 10 NM or 9999 m 2020-07-11 13:15:50 -07:00
Tim Morgan
25b8cff442 Don't use ICAO phraseology for US airports 2020-07-11 13:03:41 -07:00
Tim Morgan
9f7471178b Add option to suppress reporting QFE 2020-07-11 13:03:41 -07:00
Tim Morgan
f3f3406d64 Reorder ATIS components to match real-life ATIS, add option to suppress all but Zulu times 2020-07-11 13:03:40 -07:00
Tim Morgan
4eb886d0f3 Typo fix 2020-07-11 13:03:40 -07:00
Frank
5c6e50e7f9 Update SpawnStatic.lua
- Fixed #1335
2020-06-11 23:44:19 +02:00
Frank
fb3115a0f1 The Channel Update
**AIRBASE**
- Added "The Channel" map airbase name enumerators.

**ATIS v0.8.0**
- Added sunset and sunrise times (required new sound files).
- Supports "The Channel" map (needs new sound files).

**UTILS**
- Corrected NTTR local time diff to GMT-8 (was -7) hours.
- Corrected Normany local time diff to GMT+0 (was -1) hours.
- Added "The Channel" map local time diff as GMT+2 (should be GMT).
- Fixed bug in UTILS.GMTToLocalTimeDifference() function.
2020-06-04 23:46:26 +02:00
Frank
833d4f7b65 SPAWNSTATIC class reworked
**SPAWNSTATIC**
- Added option to link statics to units, e.g. carriers.
- Fixed SpawnFromType function.
- Added :InitXYZ functions to set parameters.
- Removed ReSpawn functions. Pointless here. Use Respawn of STATIC class instead.
- Updated docs.

**COORDINATE**
- Added optional parameter to IsDay() and IsNight() functions to check on specific time.

**STATIC**
- Fixed Respawn functions so that statics do not appear on top of each other.

**UTILS**
- Added optional parameter to GetMissionDay and GetMissionDayOfYear functions.

**AIRBOSS**
- Adjusted recovery turn time interval back to 5 min in :SetRecoveryTurnTime() function.
2020-05-27 22:18:18 +02:00
Frank
61bb59d8b3 Astro Update
**COORDINATE**
* Added functions to get sunrise and sunset times.

**UTILS**
* Added functions to calculate sunset and sunrise
* Added function to replace illegal characters.

**AIRBOSS v1.1.5**
* Fixed wrong function name when recovery is resumed.
* Replace illegcal characters in player names when writing trapsheet.
2020-05-24 23:33:21 +02:00
Frank
21652de804 AIRBOSS v1.1.4
Updated Nimitz class parameters.
2020-05-20 23:03:25 +02:00
34 changed files with 6263 additions and 2903 deletions

View File

@@ -2774,7 +2774,7 @@ do -- AI_A2A_DISPATCHER
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
--
-- -- Now Setup the default fuel treshold.
-- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- A2ADispatcher:SetSquadronFuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
--
function AI_A2A_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
@@ -2817,7 +2817,7 @@ do -- AI_A2A_DISPATCHER
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
--
-- -- Now Setup the squadron fuel treshold.
-- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- A2ADispatcher:SetSquadronFuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
--
-- -- Now Setup the squadron tanker.
-- A2ADispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.

View File

@@ -164,7 +164,7 @@ AI_FORMATION.__Enum.ReportType = {
--- MENUPARAM type
-- @type MENUPARAM
-- @field #AI_FORMATION ParamSelf
-- @field #Distance ParamDistance
-- @field #number ParamDistance
-- @field #function ParamFunction
-- @field #string ParamMessage
@@ -184,11 +184,13 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
self.FollowGroupSet:ForEachGroup(
function( FollowGroup )
self:E("Following")
--self:E("Following")
FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
end
)
self:SetFlightModeFormation()
self:SetFlightRandomization( 2 )
self:SetStartState( "None" )
@@ -207,9 +209,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 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 #boolean
@@ -222,9 +224,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 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 AI_FORMATION
@@ -232,9 +234,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 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 AI_FORMATION
@@ -243,9 +245,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 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", "*" )
@@ -257,7 +259,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @return #boolean
--- FormationTrail Handler OnAfter for AI_FORMATION
@@ -268,14 +270,14 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
--- FormationTrail Trigger for AI_FORMATION
-- @function [parent=#AI_FORMATION] FormationTrail
-- @param #AI_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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
--- FormationTrail Asynchronous Trigger for AI_FORMATION
-- @function [parent=#AI_FORMATION] __FormationTrail
@@ -283,7 +285,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
self:AddTransition( "*", "FormationStack", "*" )
--- FormationStack Handler OnBefore for AI_FORMATION
@@ -294,7 +296,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @return #boolean
@@ -306,7 +308,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
--- FormationStack Trigger for AI_FORMATION
@@ -314,7 +316,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
--- FormationStack Asynchronous Trigger for AI_FORMATION
@@ -323,7 +325,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
self:AddTransition( "*", "FormationLeftLine", "*" )
@@ -335,8 +337,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 #boolean
@@ -348,16 +350,16 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 AI_FORMATION
-- @function [parent=#AI_FORMATION] FormationLeftLine
-- @param #AI_FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 AI_FORMATION
@@ -365,8 +367,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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", "*" )
@@ -378,8 +380,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 #boolean
@@ -391,16 +393,16 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 AI_FORMATION
-- @function [parent=#AI_FORMATION] FormationRightLine
-- @param #AI_FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 AI_FORMATION
@@ -408,8 +410,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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", "*" )
@@ -422,8 +424,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 #boolean
@@ -436,8 +438,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationLeftWing Trigger for AI_FORMATION
@@ -445,8 +447,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationLeftWing Asynchronous Trigger for AI_FORMATION
@@ -455,8 +457,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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( "*", "FormationRightWing", "*" )
@@ -469,8 +471,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 #boolean
@@ -483,8 +485,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationRightWing Trigger for AI_FORMATION
@@ -492,8 +494,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationRightWing Asynchronous Trigger for AI_FORMATION
@@ -502,8 +504,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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( "*", "FormationCenterWing", "*" )
@@ -516,9 +518,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 #boolean
@@ -531,9 +533,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationCenterWing Trigger for AI_FORMATION
@@ -541,9 +543,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationCenterWing Asynchronous Trigger for AI_FORMATION
@@ -552,9 +554,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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( "*", "FormationVic", "*" )
@@ -566,9 +568,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 #boolean
@@ -580,9 +582,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationVic Trigger for AI_FORMATION
@@ -590,9 +592,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
--- FormationVic Asynchronous Trigger for AI_FORMATION
@@ -601,9 +603,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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( "*", "FormationBox", "*" )
@@ -615,9 +617,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
-- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #boolean
@@ -630,9 +632,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
-- @param #number ZLevels The amount of levels on the Z-axis.
@@ -641,9 +643,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @param #AI_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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
-- @param #number ZLevels The amount of levels on the Z-axis.
@@ -653,9 +655,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
-- @param #number ZLevels The amount of levels on the Z-axis.
@@ -704,9 +706,9 @@ end
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 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 #AI_FORMATION
function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation ) --R2.1
@@ -751,7 +753,7 @@ end
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1
@@ -769,7 +771,7 @@ end
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1
@@ -789,8 +791,8 @@ end
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 #AI_FORMATION
function AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
@@ -808,8 +810,8 @@ end
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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 #AI_FORMATION
function AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
@@ -828,8 +830,8 @@ end
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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.
function AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
@@ -848,8 +850,8 @@ end
-- @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 #nubmer YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-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.
function AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
@@ -867,9 +869,9 @@ end
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
@@ -905,9 +907,9 @@ end
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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 #AI_FORMATION
function AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
@@ -924,9 +926,9 @@ end
-- @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 #nubmer YStart The start position on the Y-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 YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-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.
-- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #AI_FORMATION
@@ -997,7 +999,7 @@ function AI_FORMATION:SetFlightModeMission( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
else
self.EscortGroupSet:ForSomeGroupAlive(
self.FollowGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
function( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
@@ -1021,7 +1023,7 @@ function AI_FORMATION:SetFlightModeAttack( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
else
self.EscortGroupSet:ForSomeGroupAlive(
self.FollowGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
function( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
@@ -1045,7 +1047,7 @@ function AI_FORMATION:SetFlightModeFormation( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
else
self.EscortGroupSet:ForSomeGroupAlive(
self.FollowGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
function( FollowGroup )
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
@@ -1065,7 +1067,7 @@ end
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @pram #string To The to state.
-- @param #string To The to state.
function AI_FORMATION:onafterStop(FollowGroupSet, From, Event, To) --R2.1
self:E("Stopping formation.")
end
@@ -1075,7 +1077,7 @@ end
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @pram #string To The to state.
-- @param #string To The to state.
function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
if From=="Stopped" then
return false -- Deny transition.
@@ -1083,7 +1085,12 @@ function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
return true
end
--- @param #AI_FORMATION self
--- Enter following state.
-- @param #AI_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 AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
if self.FollowUnit:IsAlive() then
@@ -1093,153 +1100,184 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
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", ClientUnit:GetPointVec3() )
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 = ClientUnit:GetPointVec3()
CV2 = CuVec3
ClientUnit:SetState( self, "CT1", CT2 )
ClientUnit:SetState( self, "CV1", CV2 )
end
FollowGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP FollowGroup
-- @param Wrapper.Unit#UNIT ClientUnit
function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 )
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
FollowGroup:OptionROTEvadeFire()
FollowGroup:OptionROEReturnFire()
local GroupUnit = FollowGroup:GetUnit( 1 )
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", GroupUnit:GetPointVec3() )
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 = GroupUnit:GetPointVec3()
GV2:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.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,
y = GH2.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
}
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,
self, ClientUnit, CT1, CV1, CT2, CV2
)
--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 #AI_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 AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation 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:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
]]
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,
y = GH2.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

View File

@@ -26,8 +26,6 @@
-- @module Core.Base
-- @image Core_Base.JPG
local _TraceOnOff = true
local _TraceLevel = 1
local _TraceAll = false
@@ -256,6 +254,8 @@ end
-- @param #BASE Parent is the Parent class that the Child inherits from.
-- @return #BASE Child
function BASE:Inherit( Child, Parent )
-- Create child.
local Child = routines.utils.deepCopy( Child )
if Child ~= nil then
@@ -271,6 +271,7 @@ function BASE:Inherit( Child, Parent )
--Child:_SetDestructor()
end
return Child
end
@@ -433,7 +434,7 @@ do -- Event Handling
-- reflecting the order of the classes subscribed to the Event to be processed.
-- @param #BASE self
-- @param #number EventPriority The @{Event} processing Priority.
-- @return self
-- @return #BASE self
function BASE:SetEventPriority( EventPriority )
self._.EventPriority = EventPriority
end
@@ -450,23 +451,23 @@ do -- Event Handling
--- Subscribe to a DCS Event.
-- @param #BASE self
-- @param Core.Event#EVENTS Event
-- @param Core.Event#EVENTS EventID Event ID.
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
-- @return #BASE
function BASE:HandleEvent( Event, EventFunction )
function BASE:HandleEvent( EventID, EventFunction )
self:EventDispatcher():OnEventGeneric( EventFunction, self, Event )
self:EventDispatcher():OnEventGeneric( EventFunction, self, EventID )
return self
end
--- UnSubscribe to a DCS event.
-- @param #BASE self
-- @param Core.Event#EVENTS Event
-- @param Core.Event#EVENTS EventID Event ID.
-- @return #BASE
function BASE:UnHandleEvent( Event )
function BASE:UnHandleEvent( EventID )
self:EventDispatcher():RemoveEvent( self, Event )
self:EventDispatcher():RemoveEvent( self, EventID )
return self
end

View File

@@ -1,9 +1,9 @@
--- **Core** - Manages several databases containing templates, mission objects, and mission information.
--
--- **Core** - Manages several databases containing templates, mission objects, and mission information.
--
-- ===
--
--
-- ## Features:
--
--
-- * During mission startup, scan the mission environment, and create / instantiate intelligently the different objects as defined within the mission.
-- * Manage database of DCS Group templates (as modelled using the mission editor).
-- - Group templates.
@@ -20,14 +20,14 @@
-- * Manage database of hits to units and statics.
-- * Manage database of destroys of units and statics.
-- * Manage database of @{Core.Zone#ZONE_BASE} objects.
--
--
-- ===
--
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ### Contributions:
--
-- ===
--
--
-- @module Core.Database
-- @image Core_Database.JPG
@@ -36,9 +36,9 @@
-- @extends Core.Base#BASE
--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator.
--
--
-- Mission designers can use the DATABASE class to refer to:
--
--
-- * STATICS
-- * UNITS
-- * GROUPS
@@ -47,12 +47,12 @@
-- * PLAYERSJOINED
-- * PLAYERS
-- * CARGOS
--
--
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
--
--
-- The singleton object **_DATABASE** is automatically created by MOOSE, that administers all objects within the mission.
-- Moose refers to **_DATABASE** within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
--
--
-- @field #DATABASE
DATABASE = {
ClassName = "DATABASE",
@@ -82,6 +82,9 @@ DATABASE = {
DESTROYS = {},
ZONES = {},
ZONES_GOAL = {},
WAREHOUSES = {},
FLIGHTGROUPS = {},
FLIGHTCONTROLS = {},
}
local _DATABASECoalition =
@@ -113,7 +116,7 @@ function DATABASE:New()
local self = BASE:Inherit( self, BASE:New() ) -- #DATABASE
self:SetEventPriority( 1 )
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
@@ -123,11 +126,11 @@ function DATABASE:New()
self:HandleEvent( EVENTS.DeleteCargo )
self:HandleEvent( EVENTS.NewZone )
self:HandleEvent( EVENTS.DeleteZone )
-- Follow alive players and clients
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event.
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
self:_RegisterTemplates()
self:_RegisterGroupsAndUnits()
self:_RegisterClients()
@@ -136,16 +139,16 @@ function DATABASE:New()
self:_RegisterAirbases()
self.UNITS_Position = 0
--- @param #DATABASE self
local function CheckPlayers( self )
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL )}
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
--self:E( { "CoalitionData:", CoalitionData } )
for UnitId, UnitData in pairs( CoalitionData ) do
if UnitData and UnitData:isExist() then
local UnitName = UnitData:getName()
local PlayerName = UnitData:getPlayerName()
local PlayerUnit = UNIT:Find( UnitData )
@@ -164,10 +167,10 @@ function DATABASE:New()
end
end
end
--self:E( "Scheduling" )
--PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 1, 1 )
return self
end
@@ -190,10 +193,10 @@ function DATABASE:AddUnit( DCSUnitName )
self:T( { "Add UNIT:", DCSUnitName } )
local UnitRegister = UNIT:Register( DCSUnitName )
self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName )
table.insert( self.UNITS_Index, DCSUnitName )
end
return self.UNITS[DCSUnitName]
end
@@ -202,7 +205,7 @@ end
-- @param #DATABASE self
function DATABASE:DeleteUnit( DCSUnitName )
self.UNITS[DCSUnitName] = nil
self.UNITS[DCSUnitName] = nil
end
--- Adds a Static based on the Static Name in the DATABASE.
@@ -213,7 +216,7 @@ function DATABASE:AddStatic( DCSStaticName )
self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName )
return self.STATICS[DCSStaticName]
end
return nil
end
@@ -222,7 +225,7 @@ end
-- @param #DATABASE self
function DATABASE:DeleteStatic( DCSStaticName )
--self.STATICS[DCSStaticName] = nil
--self.STATICS[DCSStaticName] = nil
end
--- Finds a STATIC based on the StaticName.
@@ -254,7 +257,7 @@ function DATABASE:AddAirbase( AirbaseName )
if not self.AIRBASES[AirbaseName] then
self.AIRBASES[AirbaseName] = AIRBASE:Register( AirbaseName )
end
return self.AIRBASES[AirbaseName]
end
@@ -264,7 +267,7 @@ end
-- @param #string AirbaseName The name of the airbase
function DATABASE:DeleteAirbase( AirbaseName )
self.AIRBASES[AirbaseName] = nil
self.AIRBASES[AirbaseName] = nil
end
--- Finds an AIRBASE based on the AirbaseName.
@@ -285,29 +288,29 @@ do -- Zones
-- @param #string ZoneName The name of the zone.
-- @return Core.Zone#ZONE_BASE The found ZONE.
function DATABASE:FindZone( ZoneName )
local ZoneFound = self.ZONES[ZoneName]
return ZoneFound
end
--- Adds a @{Zone} based on the zone name in the DATABASE.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
-- @param Core.Zone#ZONE_BASE Zone The zone.
function DATABASE:AddZone( ZoneName, Zone )
if not self.ZONES[ZoneName] then
self.ZONES[ZoneName] = Zone
end
end
--- Deletes a @{Zone} from the DATABASE based on the zone name.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
function DATABASE:DeleteZone( ZoneName )
self.ZONES[ZoneName] = nil
self.ZONES[ZoneName] = nil
end
@@ -324,20 +327,20 @@ do -- Zones
self.ZONENAMES[ZoneName] = ZoneName
self:AddZone( ZoneName, Zone )
end
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
if ZoneGroupName:match("#ZONE_POLYGON") then
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
self:I( { "Register ZONE_POLYGON:", Name = ZoneName } )
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
self.ZONENAMES[ZoneName] = ZoneName
self:AddZone( ZoneName, Zone_Polygon )
end
end
end
@@ -350,29 +353,29 @@ do -- Zone_Goal
-- @param #string ZoneName The name of the zone.
-- @return Core.Zone#ZONE_BASE The found ZONE.
function DATABASE:FindZoneGoal( ZoneName )
local ZoneFound = self.ZONES_GOAL[ZoneName]
return ZoneFound
end
--- Adds a @{Zone} based on the zone name in the DATABASE.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
-- @param Core.Zone#ZONE_BASE Zone The zone.
function DATABASE:AddZoneGoal( ZoneName, Zone )
if not self.ZONES_GOAL[ZoneName] then
self.ZONES_GOAL[ZoneName] = Zone
end
end
--- Deletes a @{Zone} from the DATABASE based on the zone name.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
function DATABASE:DeleteZoneGoal( ZoneName )
self.ZONES_GOAL[ZoneName] = nil
self.ZONES_GOAL[ZoneName] = nil
end
end -- Zone_Goal
@@ -382,31 +385,31 @@ do -- cargo
-- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:AddCargo( Cargo )
if not self.CARGOS[Cargo.Name] then
self.CARGOS[Cargo.Name] = Cargo
end
end
--- Deletes a Cargo from the DATABASE based on the Cargo Name.
-- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:DeleteCargo( CargoName )
self.CARGOS[CargoName] = nil
self.CARGOS[CargoName] = nil
end
--- Finds an CARGO based on the CargoName.
-- @param #DATABASE self
-- @param #string CargoName
-- @return Wrapper.Cargo#CARGO The found CARGO.
function DATABASE:FindCargo( CargoName )
local CargoFound = self.CARGOS[CargoName]
return CargoFound
end
--- Checks if the Template name has a #CARGO tag.
-- If yes, the group is a cargo.
-- @param #DATABASE self
@@ -415,10 +418,10 @@ do -- cargo
function DATABASE:IsCargo( TemplateName )
TemplateName = env.getValueDictByKey( TemplateName )
local Cargo = TemplateName:match( "#(CARGO)" )
return Cargo and Cargo == "CARGO"
return Cargo and Cargo == "CARGO"
end
--- Private method that registers new Static Templates within the DATABASE Object.
@@ -427,7 +430,7 @@ do -- cargo
function DATABASE:_RegisterCargos()
local Groups = UTILS.DeepCopy( self.GROUPS ) -- This is a very important statement. CARGO_GROUP:New creates a new _DATABASE.GROUP entry, which will confuse the loop. I searched 4 hours on this to find the bug!
for CargoGroupName, CargoGroup in pairs( Groups ) do
self:I( { Cargo = CargoGroupName } )
if self:IsCargo( CargoGroupName ) then
@@ -440,12 +443,12 @@ do -- cargo
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
self:I({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
end
end
for CargoStaticName, CargoStatic in pairs( self.STATICS ) do
if self:IsCargo( CargoStaticName ) then
local CargoInfo = CargoStaticName:match("#CARGO(.*)")
@@ -456,7 +459,7 @@ do -- cargo
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
if Category == "SLING" then
self:I({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
@@ -468,7 +471,7 @@ do -- cargo
end
end
end
end
end -- cargo
@@ -514,9 +517,9 @@ function DATABASE:AddGroup( GroupName )
if not self.GROUPS[GroupName] then
self:T( { "Add GROUP:", GroupName } )
self.GROUPS[GroupName] = GROUP:Register( GroupName )
end
return self.GROUPS[GroupName]
end
return self.GROUPS[GroupName]
end
--- Adds a player based on the Player Name in the DATABASE.
@@ -618,7 +621,7 @@ function DATABASE:Spawn( SpawnTemplate )
for UnitID, UnitData in pairs( SpawnTemplate.units ) do
self:AddUnit( UnitData.name )
end
return SpawnGroup
end
@@ -650,21 +653,21 @@ end
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name )
if not self.Templates.Groups[GroupTemplateName] then
self.Templates.Groups[GroupTemplateName] = {}
self.Templates.Groups[GroupTemplateName].Status = nil
end
-- Delete the spans from the route, it is not needed and takes memory.
if GroupTemplate.route and GroupTemplate.route.spans then
if GroupTemplate.route and GroupTemplate.route.spans then
GroupTemplate.route.spans = nil
end
GroupTemplate.CategoryID = CategoryID
GroupTemplate.CoalitionID = CoalitionSide
GroupTemplate.CountryID = CountryID
self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName
self.Templates.Groups[GroupTemplateName].Template = GroupTemplate
self.Templates.Groups[GroupTemplateName].groupId = GroupTemplate.groupId
@@ -679,7 +682,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do
UnitTemplate.name = env.getValueDictByKey(UnitTemplate.name)
self.Templates.Units[UnitTemplate.name] = {}
self.Templates.Units[UnitTemplate.name].UnitName = UnitTemplate.name
self.Templates.Units[UnitTemplate.name].Template = UnitTemplate
@@ -697,8 +700,8 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
end
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
end
self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
@@ -727,13 +730,13 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
local StaticTemplate = UTILS.DeepCopy( StaticTemplate )
local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name)
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
StaticTemplate.CategoryID = CategoryID
StaticTemplate.CoalitionID = CoalitionID
StaticTemplate.CountryID = CountryID
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
@@ -744,12 +747,12 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
self:I( { Static = self.Templates.Statics[StaticTemplateName].StaticName,
Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID,
Category = self.Templates.Statics[StaticTemplateName].CategoryID,
Country = self.Templates.Statics[StaticTemplateName].CountryID
Country = self.Templates.Statics[StaticTemplateName].CountryID
}
)
self:AddStatic( StaticTemplateName )
end
@@ -817,7 +820,7 @@ function DATABASE:_RegisterPlayers()
end
end
end
return self
end
@@ -833,12 +836,12 @@ function DATABASE:_RegisterGroupsAndUnits()
if DCSGroup:isExist() then
local DCSGroupName = DCSGroup:getName()
self:I( { "Register Group:", DCSGroupName } )
self:AddGroup( DCSGroupName )
for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do
local DCSUnitName = DCSUnit:getName()
self:I( { "Register Unit:", DCSUnitName } )
self:AddUnit( DCSUnitName )
@@ -846,10 +849,10 @@ function DATABASE:_RegisterGroupsAndUnits()
else
self:E( { "Group does not exist: ", DCSGroup } )
end
end
end
self:T("Groups:")
for GroupName, Group in pairs( self.GROUPS ) do
self:T( { "Group:", GroupName } )
@@ -867,7 +870,7 @@ function DATABASE:_RegisterClients()
self:T( { "Register Client:", ClientName } )
self:AddClient( ClientName )
end
return self
end
@@ -881,7 +884,7 @@ function DATABASE:_RegisterStatics()
if DCSStatic:isExist() then
local DCSStaticName = DCSStatic:getName()
self:T( { "Register Static:", DCSStaticName } )
self:AddStatic( DCSStaticName )
else
@@ -893,32 +896,26 @@ function DATABASE:_RegisterStatics()
return self
end
--- @param #DATABASE self
--- Register all world airbases.
-- @param #DATABASE self
-- @return #DATABASE self
function DATABASE:_RegisterAirbases()
--[[
local CoalitionsData = { AirbasesRed = coalition.getAirbases( coalition.side.RED ), AirbasesBlue = coalition.getAirbases( coalition.side.BLUE ), AirbasesNeutral = coalition.getAirbases( coalition.side.NEUTRAL ) }
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
for DCSAirbaseId, DCSAirbase in pairs( CoalitionData ) do
local DCSAirbaseName = DCSAirbase:getName()
self:T( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } )
self:AddAirbase( DCSAirbaseName )
end
end
]]
for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do
local DCSAirbaseName = DCSAirbase:getName()
-- This gives the incorrect value to be inserted into the airdromeID for DCS 2.5.6!
local airbaseID=DCSAirbase:getID()
local airbase=self:AddAirbase( DCSAirbaseName )
self:I(string.format("Register Airbase: %s, getID=%d, GetID=%d (unique=%d)", DCSAirbaseName, DCSAirbase:getID(), airbase:GetID(), airbase:GetID(true)))
end
-- Get the airbase name.
local DCSAirbaseName = DCSAirbase:getName()
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
local airbaseID=DCSAirbase:getID()
-- Add and register airbase.
local airbase=self:AddAirbase( DCSAirbaseName )
-- Debug output.
self:I(string.format("Register Airbase: %s, getID=%d, GetID=%d (unique=%d)", DCSAirbaseName, DCSAirbase:getID(), airbase:GetID(), airbase:GetID(true)))
end
return self
end
@@ -934,7 +931,7 @@ function DATABASE:_EventOnBirth( Event )
if Event.IniDCSUnit then
if Event.IniObjectCategory == 3 then
self:AddStatic( Event.IniDCSUnitName )
self:AddStatic( Event.IniDCSUnitName )
else
if Event.IniObjectCategory == 1 then
self:AddUnit( Event.IniDCSUnitName )
@@ -976,7 +973,7 @@ function DATABASE:_EventOnDeadOrCrash( Event )
if Event.IniObjectCategory == 3 then
if self.STATICS[Event.IniDCSUnitName] then
self:DeleteStatic( Event.IniDCSUnitName )
end
end
else
if Event.IniObjectCategory == 1 then
if self.UNITS[Event.IniDCSUnitName] then
@@ -985,7 +982,7 @@ function DATABASE:_EventOnDeadOrCrash( Event )
end
end
end
self:AccountDestroys( Event )
end
@@ -1039,7 +1036,7 @@ end
-- @return #DATABASE self
function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
self:F2( arg )
local function CoRoutine()
local Count = 0
for ObjectID, Object in pairs( Set ) do
@@ -1048,20 +1045,20 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
Count = Count + 1
-- if Count % 100 == 0 then
-- coroutine.yield( false )
-- end
-- end
end
return true
end
-- local co = coroutine.create( CoRoutine )
local co = CoRoutine
local function Schedule()
-- local status, res = coroutine.resume( co )
local status, res = co()
self:T3( { status, res } )
if status == false then
error( res )
end
@@ -1076,7 +1073,7 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
--local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 )
Schedule()
return self
end
@@ -1087,7 +1084,7 @@ end
-- @return #DATABASE self
function DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... ) --R2.1
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.STATICS )
return self
@@ -1100,7 +1097,7 @@ end
-- @return #DATABASE self
function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.UNITS )
return self
@@ -1113,7 +1110,7 @@ end
-- @return #DATABASE self
function DATABASE:ForEachGroup( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.GROUPS )
return self
@@ -1126,9 +1123,9 @@ end
-- @return #DATABASE self
function DATABASE:ForEachPlayer( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERS )
return self
end
@@ -1139,9 +1136,9 @@ end
-- @return #DATABASE self
function DATABASE:ForEachPlayerJoined( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERSJOINED )
return self
end
@@ -1151,9 +1148,9 @@ end
-- @return #DATABASE self
function DATABASE:ForEachPlayerUnit( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERUNITS )
return self
end
@@ -1164,7 +1161,7 @@ end
-- @return #DATABASE self
function DATABASE:ForEachClient( IteratorFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, arg, self.CLIENTS )
return self
@@ -1176,7 +1173,7 @@ end
-- @return #DATABASE self
function DATABASE:ForEachCargo( IteratorFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, arg, self.CARGOS )
return self
@@ -1252,8 +1249,44 @@ function DATABASE:SetPlayerSettings( PlayerName, Settings )
self.PLAYERSETTINGS[PlayerName] = Settings
end
--- Add a flight group to the data base.
-- @param #DATABASE self
-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup
function DATABASE:AddFlightGroup(flightgroup)
self:I({NewFlightGroup=flightgroup.groupname})
self.FLIGHTGROUPS[flightgroup.groupname]=flightgroup
end
--- Get a flight group from the data base.
-- @param #DATABASE self
-- @param #string groupname Group name of the flight group. Can also be passed as GROUP object.
-- @return Ops.FlightGroup#FLIGHTGROUP Flight group object.
function DATABASE:GetFlightGroup(groupname)
-- Get group and group name.
if type(groupname)=="string" then
else
groupname=groupname:GetName()
end
return self.FLIGHTGROUPS[groupname]
end
--- Add a flight control to the data base.
-- @param #DATABASE self
-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol
function DATABASE:AddFlightControl(flightcontrol)
self:F2( { flightcontrol } )
self.FLIGHTCONTROLS[flightcontrol.airbasename]=flightcontrol
end
--- Get a flight control object from the data base.
-- @param #DATABASE self
-- @param #string airbasename Name of the associated airbase.
-- @return Ops.FlightControl#FLIGHTCONTROL The FLIGHTCONTROL object.s
function DATABASE:GetFlightControl(airbasename)
return self.FLIGHTCONTROLS[airbasename]
end
--- @param #DATABASE self
function DATABASE:_RegisterTemplates()
@@ -1267,7 +1300,7 @@ function DATABASE:_RegisterTemplates()
if (CoalitionName == 'red' or CoalitionName == 'blue' or CoalitionName == 'neutrals') and type(coa_data) == 'table' then
--self.Units[coa_name] = {}
local CoalitionSide = coalition.side[string.upper(CoalitionName)]
if CoalitionName=="red" then
CoalitionSide=coalition.side.RED
@@ -1300,10 +1333,10 @@ function DATABASE:_RegisterTemplates()
local CountryName = string.upper(cntry_data.name)
local CountryID = cntry_data.id
self.COUNTRY_ID[CountryName] = CountryID
self.COUNTRY_NAME[CountryID] = CountryName
--self.Units[coa_name][countryName] = {}
--self.Units[coa_name][countryName]["countryId"] = cntry_data.id
@@ -1322,18 +1355,18 @@ function DATABASE:_RegisterTemplates()
for group_num, Template in pairs(obj_type_data.group) do
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
self:_RegisterGroupTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID
self:_RegisterGroupTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID
)
else
self:_RegisterStaticTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID
self:_RegisterStaticTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID
)
end --if GroupTemplate and GroupTemplate.units then
end --for group_num, GroupTemplate in pairs(obj_type_data.group) do
@@ -1354,35 +1387,35 @@ end
-- @param Core.Event#EVENTDATA Event
function DATABASE:AccountHits( Event )
self:F( { Event } )
if Event.IniPlayerName ~= nil then -- It is a player that is hitting something
self:T( "Hitting Something" )
-- What is he hitting?
if Event.TgtCategory then
-- A target got hit
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
local Hit = self.HITS[Event.TgtUnitName]
Hit.Players = Hit.Players or {}
Hit.Players[Event.IniPlayerName] = true
end
end
-- 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
if Event.WeaponPlayerName ~= nil then
self:T( "Hitting Scenery" )
-- What is he hitting?
if Event.TgtCategory then
if Event.WeaponCoalition then -- A coalition object was hit, probably a static.
-- A target got hit
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
local Hit = self.HITS[Event.TgtUnitName]
Hit.Players = Hit.Players or {}
Hit.Players[Event.WeaponPlayerName] = true
else -- A scenery object was hit.
@@ -1390,13 +1423,13 @@ end
end
end
end
--- Account the destroys.
-- @param #DATABASE self
-- @param Core.Event#EVENTDATA Event
function DATABASE:AccountDestroys( Event )
self:F( { Event } )
local TargetUnit = nil
local TargetGroup = nil
local TargetUnitName = ""
@@ -1408,26 +1441,26 @@ end
local TargetUnitCoalition = nil
local TargetUnitCategory = nil
local TargetUnitType = nil
if Event.IniDCSUnit then
TargetUnit = Event.IniUnit
TargetUnitName = Event.IniDCSUnitName
TargetGroup = Event.IniDCSGroup
TargetGroupName = Event.IniDCSGroupName
TargetPlayerName = Event.IniPlayerName
TargetCoalition = Event.IniCoalition
--TargetCategory = TargetUnit:getCategory()
--TargetCategory = TargetUnit:getDesc().category -- Workaround
TargetCategory = Event.IniCategory
TargetType = Event.IniTypeName
TargetUnitType = TargetType
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } )
end
local Destroyed = false
-- What is the player destroying?
@@ -1436,8 +1469,3 @@ end
self.DESTROYS[Event.IniUnitName] = true
end
end

View File

@@ -526,17 +526,21 @@ local _EVENTMETA = {
-- @param #EVENT self
-- @return #EVENT self
function EVENT:New()
-- Inherit base.
local self = BASE:Inherit( self, BASE:New() )
self:F2()
self.EventHandler = world.addEventHandler( self )
-- Add world event handler.
self.EventHandler = world.addEventHandler(self)
return self
end
--- Initializes the Events structure for the event
--- Initializes the Events structure for the event.
-- @param #EVENT self
-- @param DCS#world.event EventID
-- @param Core.Base#BASE EventClass
-- @param DCS#world.event EventID Event ID.
-- @param Core.Base#BASE EventClass The class object for which events are handled.
-- @return #EVENT.Events
function EVENT:Init( EventID, EventClass )
self:F3( { _EVENTMETA[EventID].Text, EventClass } )
@@ -548,6 +552,7 @@ function EVENT:Init( EventID, EventClass )
-- Each event has a subtable of EventClasses, ordered by EventPriority.
local EventPriority = EventClass:GetEventPriority()
if not self.Events[EventID][EventPriority] then
self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } )
end
@@ -555,40 +560,45 @@ function EVENT:Init( EventID, EventClass )
if not self.Events[EventID][EventPriority][EventClass] then
self.Events[EventID][EventPriority][EventClass] = {}
end
return self.Events[EventID][EventPriority][EventClass]
end
--- Removes a subscription
-- @param #EVENT self
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID
-- @return #EVENT.Events
-- @param DCS#world.event EventID Event ID.
-- @return #EVENT self
function EVENT:RemoveEvent( EventClass, EventID )
-- Debug info.
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
-- Get event prio.
local EventPriority = EventClass:GetEventPriority()
-- Events.
self.Events = self.Events or {}
self.Events[EventID] = self.Events[EventID] or {}
self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {}
-- Remove
self.Events[EventID][EventPriority][EventClass] = nil
return self
end
--- Resets subscriptions
--- Resets subscriptions.
-- @param #EVENT self
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID
-- @param DCS#world.event EventID Event ID.
-- @return #EVENT.Events
function EVENT:Reset( EventObject ) --R2.1
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
local EventPriority = EventObject:GetEventPriority()
for EventID, EventData in pairs( self.Events ) do
if self.EventsDead then
if self.EventsDead[EventID] then
@@ -603,19 +613,22 @@ function EVENT:Reset( EventObject ) --R2.1
end
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
-- @param #EVENT self
-- @param Core.Base#BASE EventObject
function EVENT:RemoveAll( EventObject )
self:F3( { EventObject:GetClassNameAndID() } )
-- @param Core.Base#BASE EventClass The self class object for which the events are removed.
-- @return #EVENT self
function EVENT:RemoveAll(EventClass)
local EventClass = EventObject:GetClassNameAndID()
local EventClassName = EventClass:GetClassNameAndID()
-- Get Event prio.
local EventPriority = EventClass:GetEventPriority()
for EventID, EventData in pairs( self.Events ) do
self.Events[EventID][EventPriority][EventClass] = nil
end
return self
end
@@ -636,7 +649,7 @@ function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, Eve
return self
end
--- Set a new listener for an S_EVENT_X event independent from a unit or a weapon.
--- Set a new listener for an `S_EVENT_X` event independent from a unit or a weapon.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is captured. When the event happens, the event process will be called in this class provided.
@@ -652,13 +665,13 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
end
--- Set a new listener for an S_EVENT_X event for a UNIT.
--- Set a new listener for an `S_EVENT_X` event for a UNIT.
-- @param #EVENT self
-- @param #string UnitName The name of the UNIT.
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param EventID
-- @return #EVENT
-- @return #EVENT self
function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID )
self:F2( UnitName )
@@ -724,10 +737,10 @@ do -- OnDead
--- Create an OnDead event handler for a group
-- @param #EVENT self
-- @param Wrapper.Group#GROUP EventGroup
-- @param Wrapper.Group#GROUP EventGroup The GROUP object.
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param EventClass The self instance of the class for which the event is.
-- @return #EVENT
-- @param #table EventClass The self instance of the class for which the event is.
-- @return #EVENT self
function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name )
@@ -740,12 +753,13 @@ end
do -- OnLand
--- Create an OnLand event handler for a group
-- @param #EVENT self
-- @param #table EventTemplate
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param EventClass The self instance of the class for which the event is.
-- @return #EVENT
-- @param #table EventClass The self instance of the class for which the event is.
-- @return #EVENT self
function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name )
@@ -757,12 +771,13 @@ do -- OnLand
end
do -- OnTakeOff
--- Create an OnTakeOff event handler for a group
-- @param #EVENT self
-- @param #table EventTemplate
-- @param #table EventTemplate Template table.
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param EventClass The self instance of the class for which the event is.
-- @return #EVENT
-- @param #table EventClass The self instance of the class for which the event is.
-- @return #EVENT self
function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name )
@@ -902,8 +917,9 @@ do -- Event Creation
end
--- @param #EVENT self
-- @param #EVENTDATA Event
--- Main event function.
-- @param #EVENT self
-- @param #EVENTDATA Event Event data table.
function EVENT:onEvent( Event )
local ErrorHandler = function( errmsg )
@@ -1001,6 +1017,16 @@ function EVENT:onEvent( Event )
Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
end
if Event.IniObjectCategory == Object.Category.BASE then
Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
end
end
if Event.target then
@@ -1277,7 +1303,7 @@ function EVENT:onEvent( Event )
Event = nil
end
--- The EVENTHANDLER structure
--- The EVENTHANDLER structure.
-- @type EVENTHANDLER
-- @extends Core.Base#BASE
EVENTHANDLER = {
@@ -1285,7 +1311,7 @@ EVENTHANDLER = {
ClassID = 0,
}
--- The EVENTHANDLER constructor
--- The EVENTHANDLER constructor.
-- @param #EVENTHANDLER self
-- @return #EVENTHANDLER self
function EVENTHANDLER:New()

View File

@@ -71,7 +71,7 @@
--
--
-- ### Author: **FlightControl**
-- ### Contributions:
-- ### Contributions: **funkyfranky**
--
-- ===
--
@@ -81,6 +81,12 @@
do -- FSM
--- @type FSM
-- @field #string ClassName Name of the class.
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
-- @field #table options Options.
-- @field #table subs Subs.
-- @field #table Scores Scores.
-- @field #string current Current state name.
-- @extends Core.Base#BASE
@@ -369,8 +375,7 @@ do -- FSM
self._EventSchedules = {}
self.CallScheduler = SCHEDULER:New( self )
return self
end
@@ -379,7 +384,6 @@ do -- FSM
-- @param #FSM self
-- @param #string State A string defining the start state.
function FSM:SetStartState( State )
self._StartState = State
self.current = State
end
@@ -389,7 +393,6 @@ do -- FSM
-- @param #FSM self
-- @return #string A string containing the start state.
function FSM:GetStartState()
return self._StartState or {}
end
@@ -406,6 +409,7 @@ do -- FSM
Transition.Event = Event
Transition.To = To
-- Debug message.
self:T2( Transition )
self._Transitions[Transition] = Transition
@@ -414,9 +418,9 @@ do -- FSM
--- Returns a table of the transition rules defined within the FSM.
-- @return #table
function FSM:GetTransitions()
-- @param #FSM self
-- @return #table Transitions.
function FSM:GetTransitions()
return self._Transitions or {}
end
@@ -448,7 +452,8 @@ do -- FSM
--- Returns a table of the SubFSM rules defined within the FSM.
-- @return #table
-- @param #FSM self
-- @return #table Sub processes.
function FSM:GetProcesses()
self:F( { Processes = self._Processes } )
@@ -480,15 +485,17 @@ do -- FSM
end
--- Adds an End state.
function FSM:AddEndState( State )
-- @param #FSM self
-- @param #string State The FSM state.
function FSM:AddEndState( State )
self._EndStates[State] = State
self.endstates[State] = State
end
--- Returns the End states.
function FSM:GetEndStates()
-- @param #FSM self
-- @return #table End states.
function FSM:GetEndStates()
return self._EndStates or {}
end
@@ -532,18 +539,22 @@ do -- FSM
end
--- Returns a table with the scores defined.
function FSM:GetScores()
-- @param #FSM self
-- @param #table Scores.
function FSM:GetScores()
return self._Scores or {}
end
--- Returns a table with the Subs defined.
function FSM:GetSubs()
-- @param #FSM self
-- @return #table Sub processes.
function FSM:GetSubs()
return self.options.subs
end
--- Load call backs.
-- @param #FSM self
-- @param #table CallBackTable Table of call backs.
function FSM:LoadCallBacks( CallBackTable )
for name, callback in pairs( CallBackTable or {} ) do
@@ -551,21 +562,34 @@ do -- FSM
end
end
--- Event map.
-- @param #FSM self
-- @param #table Events Events.
-- @param #table EventStructure Event structure.
function FSM:_eventmap( Events, EventStructure )
local Event = EventStructure.Event
local __Event = "__" .. EventStructure.Event
self[Event] = self[Event] or self:_create_transition(Event)
self[__Event] = self[__Event] or self:_delayed_transition(Event)
-- Debug message.
self:T2( "Added methods: " .. Event .. ", " .. __Event )
Events[Event] = self.Events[Event] or { map = {} }
self:_add_to_map( Events[Event].map, EventStructure )
end
--- Sub maps.
-- @param #FSM self
-- @param #table subs Subs.
-- @param #table sub Sub.
-- @param #string name Name.
function FSM:_submap( subs, sub, name )
--self:F( { sub = sub, name = name } )
subs[sub.From] = subs[sub.From] or {}
subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {}
@@ -578,22 +602,24 @@ do -- FSM
subs[sub.From][sub.Event][sub].ReturnEvents = sub.ReturnEvents or {} -- these events need to be given to find the correct continue event ... if none given, the processing will stop.
subs[sub.From][sub.Event][sub].name = name
subs[sub.From][sub.Event][sub].fsmparent = self
end
--- Call handler.
-- @param #FSM self
-- @param #string step Step "onafter", "onbefore", "onenter", "onleave".
-- @param #string trigger Trigger.
-- @param #table params Parameters.
-- @param #string EventName Event name.
-- @return Value.
function FSM:_call_handler( step, trigger, params, EventName )
--env.info(string.format("FF T=%.3f _call_handler step=%s, trigger=%s, event=%s", timer.getTime(), step, trigger, EventName))
local handler = step .. trigger
local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg )
if BASE.Debug ~= nil then
env.info( BASE.Debug.traceback() )
end
return errmsg
end
if self[handler] then
--[[
if step == "onafter" or step == "OnAfter" then
self:T( ":::>" .. step .. params[2] .. " : " .. params[1] .. " >> " .. params[2] .. ">" .. step .. params[2] .. "()" .. " >> " .. params[3] )
elseif step == "onbefore" or step == "OnBefore" then
@@ -604,14 +630,33 @@ do -- FSM
self:T( ":::>" .. step .. params[1] .. " : " .. params[1] .. ">" .. step .. params[1] .. "()" .. " >> " .. params[2] .. " >> " .. params[3] )
else
self:T( ":::>" .. step .. " : " .. params[1] .. " >> " .. params[2] .. " >> " .. params[3] )
end
end
]]
self._EventSchedules[EventName] = nil
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
return Value
-- Error handler.
local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg )
if BASE.Debug ~= nil then
env.info( BASE.Debug.traceback() )
end
return errmsg
end
--return self[handler](self, unpack( params ))
-- Protected call.
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
return Value
end
end
--- @param #FSM self
--- Handler.
-- @param #FSM self
-- @param #string EventName Event name.
-- @param ... Arguments.
function FSM._handler( self, EventName, ... )
local Can, To = self:can( EventName )
@@ -621,7 +666,11 @@ do -- FSM
end
if Can then
-- From state.
local From = self.current
-- Parameters.
local Params = { From, EventName, To, ... }
@@ -632,8 +681,8 @@ do -- FSM
self["onafter".. EventName] or
self["OnAfter".. EventName] or
self["onenter".. To] or
self["OnEnter".. To]
then
self["OnEnter".. To] then
if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName )
return false
@@ -653,8 +702,11 @@ do -- FSM
end
end
end
else
local ClassName = self:GetClassName()
if ClassName == "FSM" then
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To )
end
@@ -672,46 +724,56 @@ do -- FSM
end
end
-- New current state.
self.current = To
local execute = true
local subtable = self:_gosub( From, EventName )
for _, sub in pairs( subtable ) do
--if sub.nextevent then
-- self:F2( "nextevent = " .. sub.nextevent )
-- self[sub.nextevent]( self )
--end
self:T( "*** FSM *** Sub *** " .. sub.StartEvent )
sub.fsm.fsmparent = self
sub.fsm.ReturnEvents = sub.ReturnEvents
sub.fsm[sub.StartEvent]( sub.fsm )
execute = false
end
local fsmparent, Event = self:_isendstate( To )
if fsmparent and Event then
self:T( "*** FSM *** End *** " .. Event )
self:_call_handler("onenter", To, Params, EventName )
self:_call_handler("OnEnter", To, Params, EventName )
self:_call_handler("onafter", EventName, Params, EventName )
self:_call_handler("OnAfter", EventName, Params, EventName )
self:_call_handler("onstate", "change", Params, EventName )
fsmparent[Event]( fsmparent )
execute = false
end
if execute then
self:_call_handler("onafter", EventName, Params, EventName )
self:_call_handler("OnAfter", EventName, Params, EventName )
-- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute!
--if from ~= to then
self:_call_handler("onenter", To, Params, EventName )
self:_call_handler("OnEnter", To, Params, EventName )
--end
self:_call_handler("onafter", EventName, Params, EventName )
self:_call_handler("OnAfter", EventName, Params, EventName )
self:_call_handler("onenter", To, Params, EventName )
self:_call_handler("OnEnter", To, Params, EventName )
self:_call_handler("onstate", "change", Params, EventName )
end
else
self:T( "*** FSM *** NO Transition *** " .. self.current .. " --> " .. EventName .. " --> ? " )
@@ -719,37 +781,68 @@ do -- FSM
return nil
end
--- Delayed transition.
-- @param #FSM self
-- @param #string EventName Event name.
-- @return #function Function.
function FSM:_delayed_transition( EventName )
return function( self, DelaySeconds, ... )
-- Debug.
self:T2( "Delayed Event: " .. EventName )
local CallID = 0
if DelaySeconds ~= nil then
if DelaySeconds < 0 then -- Only call the event ONCE!
DelaySeconds = math.abs( DelaySeconds )
if not self._EventSchedules[EventName] then
if not self._EventSchedules[EventName] then
-- Call _handler.
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
-- Set call ID.
self._EventSchedules[EventName] = CallID
-- Debug output.
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
else
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds))
-- reschedule
end
else
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
self:T2(string.format("Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
end
else
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
end
-- Debug.
self:T2( { CallID = CallID } )
end
end
--- Create transition.
-- @param #FSM self
-- @param #string EventName Event name.
-- @return #function Function.
function FSM:_create_transition( EventName )
return function( self, ... ) return self._handler( self, EventName , ... ) end
end
--- Go sub.
-- @param #FSM self
-- @param #string ParentFrom Parent from state.
-- @param #string ParentEvent Parent event name.
-- @return #table Subs.
function FSM:_gosub( ParentFrom, ParentEvent )
local fsmtable = {}
if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then
@@ -759,9 +852,15 @@ do -- FSM
return {}
end
end
--- Is end state.
-- @param #FSM self
-- @param #string Current Current state name.
-- @return #table FSM parent.
-- @return #string Event name.
function FSM:_isendstate( Current )
local FSMParent = self.fsmparent
if FSMParent and self.endstates[Current] then
--self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
FSMParent.current = Current
@@ -778,9 +877,14 @@ do -- FSM
return nil
end
--- Add to map.
-- @param #FSM self
-- @param #table Map Map.
-- @param #table Event Event table.
function FSM:_add_to_map( Map, Event )
self:F3( { Map, Event } )
if type(Event.From) == 'string' then
Map[Event.From] = Event.To
else
@@ -788,33 +892,60 @@ do -- FSM
Map[From] = Event.To
end
end
self:T3( { Map, Event } )
end
--- Get current state.
-- @param #FSM self
-- @return #string Current FSM state.
function FSM:GetState()
return self.current
end
--- Get current state.
-- @param #FSM self
-- @return #string Current FSM state.
function FSM:GetCurrentState()
return self.current
end
--- Check if FSM is in state.
-- @param #FSM self
-- @param #string State State name.
-- @param #boolean If true, FSM is in this state.
function FSM:Is( State )
return self.current == State
end
--- Check if FSM is in state.
-- @param #FSM self
-- @param #string State State name.
-- @param #boolean If true, FSM is in this state.
function FSM:is(state)
return self.current == state
end
--- Check if can do an event.
-- @param #FSM self
-- @param #string e Event name.
-- @return #boolean If true, FSM can do the event.
-- @return #string To state.
function FSM:can(e)
local Event = self.Events[e]
self:F3( { self.current, Event } )
--self:F3( { self.current, Event } )
local To = Event and Event.map[self.current] or Event.map['*']
return To ~= nil, To
end
--- Check if cannot do an event.
-- @param #FSM self
-- @param #string e Event name.
-- @return #boolean If true, FSM cannot do the event.
function FSM:cannot(e)
return not self:can(e)
end

View File

@@ -175,10 +175,6 @@ do -- COORDINATE
-- In order to use the most optimal road system to transport vehicles, the method @{#COORDINATE.GetPathOnRoad}() will calculate
-- the most optimal path following the road between two coordinates.
--
--
--
--
--
-- ## 8) Metric or imperial system
--
-- * @{#COORDINATE.IsMetric}(): Returns if the 3D point is Metric or Nautical Miles.
@@ -204,23 +200,23 @@ do -- COORDINATE
--- @field COORDINATE.WaypointAction
COORDINATE.WaypointAction = {
TurningPoint = "Turning Point",
FlyoverPoint = "Fly Over Point",
FromParkingArea = "From Parking Area",
TurningPoint = "Turning Point",
FlyoverPoint = "Fly Over Point",
FromParkingArea = "From Parking Area",
FromParkingAreaHot = "From Parking Area Hot",
FromRunway = "From Runway",
Landing = "Landing",
LandingReFuAr = "LandingReFuAr",
FromRunway = "From Runway",
Landing = "Landing",
LandingReFuAr = "LandingReFuAr",
}
--- @field COORDINATE.WaypointType
COORDINATE.WaypointType = {
TakeOffParking = "TakeOffParking",
TakeOffParking = "TakeOffParking",
TakeOffParkingHot = "TakeOffParkingHot",
TakeOff = "TakeOffParkingHot",
TurningPoint = "Turning Point",
Land = "Land",
LandingReFuAr = "LandingReFuAr",
TakeOff = "TakeOffParkingHot",
TurningPoint = "Turning Point",
Land = "Land",
LandingReFuAr = "LandingReFuAr",
}
@@ -232,6 +228,7 @@ do -- COORDINATE
-- @return #COORDINATE
function COORDINATE:New( x, y, z )
--env.info("FF COORDINATE New")
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
self.x = x
self.y = y
@@ -303,6 +300,45 @@ do -- COORDINATE
return { x = self.x, y = self.z }
end
--- Update x,y,z coordinates from a given 3D vector.
-- @param #COORDINATE self
-- @param DCS#Vec3 Vec3 The 3D vector with x,y,z components.
-- @return #COORDINATE The modified COORDINATE itself.
function COORDINATE:UpdateFromVec3(Vec3)
self.x=Vec3.x
self.y=Vec3.y
self.z=Vec3.z
return self
end
--- Update x,y,z coordinates from another given COORDINATE.
-- @param #COORDINATE self
-- @param #COORDINATE Coordinate The coordinate with the new x,y,z positions.
-- @return #COORDINATE The modified COORDINATE itself.
function COORDINATE:UpdateFromCoordinate(Coordinate)
self.x=Coordinate.x
self.y=Coordinate.y
self.z=Coordinate.z
return self
end
--- Update x and z coordinates from a given 2D vector.
-- @param #COORDINATE self
-- @param DCS#Vec2 Vec2 The 2D vector with x,y components. x is overwriting COORDINATE.x while y is overwriting COORDINATE.z.
-- @return #COORDINATE The modified COORDINATE itself.
function COORDINATE:UpdateFromVec2(Vec2)
self.x=Vec2.x
self.z=Vec2.y
return self
end
--- Returns the coordinate from the latitude and longitude given in decimal degrees.
-- @param #COORDINATE self
-- @param #number latitude Latitude in decimal degrees.
@@ -506,19 +542,29 @@ do -- COORDINATE
-- @param DCS#Distance Distance The Distance to be added in meters.
-- @param DCS#Angle Angle The Angle in degrees. Defaults to 0 if not specified (nil).
-- @param #boolean Keepalt If true, keep altitude of original coordinate. Default is that the new coordinate is created at the translated land height.
-- @return Core.Point#COORDINATE The new calculated COORDINATE.
function COORDINATE:Translate( Distance, Angle, Keepalt )
local SX = self.x
local SY = self.z
local Radians = (Angle or 0) / 180 * math.pi
local TX = Distance * math.cos( Radians ) + SX
local TY = Distance * math.sin( Radians ) + SY
if Keepalt then
return COORDINATE:NewFromVec3( { x = TX, y=self.y, z = TY } )
-- @param #boolean Overwrite If true, overwrite the original COORDINATE with the translated one. Otherwise, create a new COODINATE.
-- @return #COORDINATE The new calculated COORDINATE.
function COORDINATE:Translate( Distance, Angle, Keepalt, Overwrite )
-- Angle in rad.
local alpha = math.rad((Angle or 0))
local x = Distance * math.cos(alpha) + self.x -- New x
local z = Distance * math.sin(alpha) + self.z -- New z
local y=Keepalt and self.y or land.getHeight({x=x, y=z})
if Overwrite then
self.x=x
self.y=y
self.z=z
return self
else
return COORDINATE:NewFromVec2( { x = TX, y = TY } )
--env.info("FF translate with NEW coordinate T="..timer.getTime())
local coord=COORDINATE:New(x, y, z)
return coord
end
end
--- Rotate coordinate in 2D (x,z) space.
@@ -716,17 +762,24 @@ do -- COORDINATE
-- Move the vector to start at the end of A.
vec=UTILS.VecAdd(self, vec)
return self:New(vec.x,vec.y,vec.z)
local coord=COORDINATE:New(vec.x,vec.y,vec.z)
return coord
end
--- Return the 2D distance in meters between the target COORDINATE and the COORDINATE.
-- @param #COORDINATE self
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
-- @return DCS#Distance Distance The distance in meters.
function COORDINATE:Get2DDistance( TargetCoordinate )
local TargetVec3 = TargetCoordinate:GetVec3()
local SourceVec3 = self:GetVec3()
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
return UTILS.VecNorm(a)
--local TargetVec3 = TargetCoordinate:GetVec3()
--local SourceVec3 = self:GetVec3()
--return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
end
--- Returns the temperature in Degrees Celsius.
@@ -1086,23 +1139,6 @@ do -- COORDINATE
return self
end
--- Add a Distance in meters from the COORDINATE horizontal plane, with the given angle, and calculate the new COORDINATE.
-- @param #COORDINATE self
-- @param DCS#Distance Distance The Distance to be added in meters.
-- @param DCS#Angle Angle The Angle in degrees.
-- @return #COORDINATE The new calculated COORDINATE.
function COORDINATE:Translate( Distance, Angle )
local SX = self.x
local SZ = self.z
local Radians = Angle / 180 * math.pi
local TX = Distance * math.cos( Radians ) + SX
local TZ = Distance * math.sin( Radians ) + SZ
return COORDINATE:New( TX, self.y, TZ )
end
--- Build an air type route point.
-- @param #COORDINATE self
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
@@ -1290,8 +1326,10 @@ do -- COORDINATE
RoutePoint.x = self.x
RoutePoint.y = self.z
RoutePoint.alt = self:GetLandHeight()+1 -- self.y
RoutePoint.alt = self:GetLandHeight()+1
RoutePoint.alt_type = COORDINATE.WaypointAltType.BARO
RoutePoint.type = "Turning Point"
RoutePoint.action = Formation or "Off Road"
RoutePoint.formation_template=""
@@ -1351,7 +1389,7 @@ do -- COORDINATE
-- @param #number Coalition (Optional) Coalition of the airbase.
-- @return Wrapper.Airbase#AIRBASE Closest Airbase to the given coordinate.
-- @return #number Distance to the closest airbase in meters.
function COORDINATE:GetClosestAirbase(Category, Coalition)
function COORDINATE:GetClosestAirbase2(Category, Coalition)
-- Get all airbases of the map.
local airbases=AIRBASE.GetAllAirbases(Coalition)
@@ -1384,6 +1422,36 @@ do -- COORDINATE
return closest,distmin
end
--- Gets the nearest airbase with respect to the current coordinates.
-- @param #COORDINATE self
-- @param #number Category (Optional) Category of the airbase. Enumerator of @{Wrapper.Airbase#AIRBASE.Category}.
-- @param #number Coalition (Optional) Coalition of the airbase.
-- @return Wrapper.Airbase#AIRBASE Closest Airbase to the given coordinate.
-- @return #number Distance to the closest airbase in meters.
function COORDINATE:GetClosestAirbase(Category, Coalition)
local a=self:GetVec3()
local distmin=math.huge
local airbase=nil
for DCSairbaseID, DCSairbase in pairs(world.getAirbases(Coalition)) do
local b=DCSairbase:getPoint()
local c=UTILS.VecSubstract(a,b)
local dist=UTILS.VecNorm(c)
--env.info(string.format("Airbase %s dist=%d category=%d", DCSairbase:getName(), dist, DCSairbase:getCategory()))
if dist<distmin and (Category==nil or Category==DCSairbase:getDesc().category) then
distmin=dist
airbase=DCSairbase
end
end
return AIRBASE:Find(airbase)
end
--- Gets the nearest parking spot.
-- @param #COORDINATE self
@@ -1526,27 +1594,8 @@ do -- COORDINATE
local coord=COORDINATE:NewFromVec2(_vec2)
Path[#Path+1]=coord
if MarkPath then
coord:MarkToAll(string.format("Path segment %d.", _i))
end
if SmokePath then
coord:SmokeGreen()
end
end
-- Mark/smoke endpoints
if IncludeEndpoints then
if MarkPath then
COORDINATE:NewFromVec2(path[1]):MarkToAll("Path Initinal Point")
COORDINATE:NewFromVec2(path[1]):MarkToAll("Path Final Point")
end
if SmokePath then
COORDINATE:NewFromVec2(path[1]):SmokeBlue()
COORDINATE:NewFromVec2(path[#path]):SmokeBlue()
end
end
else
self:E("Path is nil. No valid path on road could be found.")
GotPath=false
@@ -1557,6 +1606,23 @@ do -- COORDINATE
Path[#Path+1]=ToCoord
end
-- Mark or smoke.
if MarkPath or SmokePath then
for i,c in pairs(Path) do
local coord=c --#COORDINATE
if MarkPath then
coord:MarkToAll(string.format("Path segment %d", i))
end
if SmokePath then
if i==1 or i==#Path then
coord:SmokeBlue()
else
coord:SmokeGreen()
end
end
end
end
-- Sum up distances.
if #Path>=2 then
for i=1,#Path-1 do
@@ -1564,7 +1630,7 @@ do -- COORDINATE
end
else
-- There are cases where no path on road can be found.
return nil,nil
return nil,nil,false
end
return Path, Way, GotPath
@@ -1921,15 +1987,18 @@ do -- COORDINATE
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
-- @param #COORDINATE self
-- @param #COORDINATE ToCoordinate
-- @param #number Offset Height offset in meters. Default 2 m.
-- @return #boolean true If the ToCoordinate has LOS with the Coordinate, otherwise false.
function COORDINATE:IsLOS( ToCoordinate )
function COORDINATE:IsLOS( ToCoordinate, Offset )
Offset=Offset or 2
-- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate.
local FromVec3 = self:GetVec3()
FromVec3.y = FromVec3.y + 2
FromVec3.y = FromVec3.y + Offset
local ToVec3 = ToCoordinate:GetVec3()
ToVec3.y = ToVec3.y + 2
ToVec3.y = ToVec3.y + Offset
local IsLOS = land.isVisible( FromVec3, ToVec3 )
@@ -1969,6 +2038,296 @@ do -- COORDINATE
return InSphere
end
--- Get sun rise time for a specific date at the coordinate.
-- @param #COORDINATE self
-- @param #number Day The day.
-- @param #number Month The month.
-- @param #number Year The year.
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
-- @return #string Sunrise time, e.g. "05:41".
function COORDINATE:GetSunriseAtDate(Day, Month, Year, InSeconds)
-- Day of the year.
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
local Latitude, Longitude=self:GetLLDDM()
local Tdiff=UTILS.GMTToLocalTimeDifference()
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
if InSeconds then
return sunrise
else
return UTILS.SecondsToClock(sunrise, true)
end
end
--- Get sun rise time for a specific day of the year at the coordinate.
-- @param #COORDINATE self
-- @param #number DayOfYear The day of the year.
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
-- @return #string Sunrise time, e.g. "05:41".
function COORDINATE:GetSunriseAtDayOfYear(DayOfYear, InSeconds)
local Latitude, Longitude=self:GetLLDDM()
local Tdiff=UTILS.GMTToLocalTimeDifference()
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
if InSeconds then
return sunrise
else
return UTILS.SecondsToClock(sunrise, true)
end
end
--- Get todays sun rise time.
-- @param #COORDINATE self
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
-- @return #string Sunrise time, e.g. "05:41".
function COORDINATE:GetSunrise(InSeconds)
-- Get current day of the year.
local DayOfYear=UTILS.GetMissionDayOfYear()
-- Lat and long at this point.
local Latitude, Longitude=self:GetLLDDM()
-- GMT time diff.
local Tdiff=UTILS.GMTToLocalTimeDifference()
-- Sunrise in seconds of the day.
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
local date=UTILS.GetDCSMissionDate()
-- Debug output.
--self:I(string.format("Sun rise at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff))
if InSeconds then
return sunrise
else
return UTILS.SecondsToClock(sunrise, true)
end
end
--- Get minutes until the next sun rise at this coordinate.
-- @param #COORDINATE self
-- @param OnlyToday If true, only calculate the sun rise of today. If sun has already risen, the time in negative minutes since sunrise is reported.
-- @return #number Minutes to the next sunrise.
function COORDINATE:GetMinutesToSunrise(OnlyToday)
-- Seconds of today
local time=UTILS.SecondsOfToday()
-- Next Sunrise in seconds.
local sunrise=nil
-- Time to sunrise.
local delta=nil
if OnlyToday then
---
-- Sunrise of today
---
sunrise=self:GetSunrise(true)
delta=sunrise-time
else
---
-- Sunrise of tomorrow
---
-- Tomorrows day of the year.
local DayOfYear=UTILS.GetMissionDayOfYear()+1
local Latitude, Longitude=self:GetLLDDM()
local Tdiff=UTILS.GMTToLocalTimeDifference()
sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
delta=sunrise+UTILS.SecondsToMidnight()
end
return delta/60
end
--- Check if it is day, i.e. if the sun has risen about the horizon at this coordinate.
-- @param #COORDINATE self
-- @param #string Clock (Optional) Time in format "HH:MM:SS+D", e.g. "05:40:00+3" to check if is day at 5:40 at third day after mission start. Default is to check right now.
-- @return #boolean If true, it is day. If false, it is night time.
function COORDINATE:IsDay(Clock)
if Clock then
local Time=UTILS.ClockToSeconds(Clock)
local clock=UTILS.Split(Clock, "+")[1]
-- Tomorrows day of the year.
local DayOfYear=UTILS.GetMissionDayOfYear(Time)
local Latitude, Longitude=self:GetLLDDM()
local Tdiff=UTILS.GMTToLocalTimeDifference()
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
local time=UTILS.ClockToSeconds(clock)
-- Check if time is between sunrise and sunset.
if time>sunrise and time<=sunset then
return true
else
return false
end
else
-- Todays sun rise in sec.
local sunrise=self:GetSunrise(true)
-- Todays sun set in sec.
local sunset=self:GetSunset(true)
-- Seconds passed since midnight.
local time=UTILS.SecondsOfToday()
-- Check if time is between sunrise and sunset.
if time>sunrise and time<=sunset then
return true
else
return false
end
end
end
--- Check if it is night, i.e. if the sun has set below the horizon at this coordinate.
-- @param #COORDINATE self
-- @param #string Clock (Optional) Time in format "HH:MM:SS+D", e.g. "05:40:00+3" to check if is night at 5:40 at third day after mission start. Default is to check right now.
-- @return #boolean If true, it is night. If false, it is day time.
function COORDINATE:IsNight(Clock)
return not self:IsDay(Clock)
end
--- Get sun set time for a specific date at the coordinate.
-- @param #COORDINATE self
-- @param #number Day The day.
-- @param #number Month The month.
-- @param #number Year The year.
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
-- @return #string Sunset time, e.g. "20:41".
function COORDINATE:GetSunsetAtDate(Day, Month, Year, InSeconds)
-- Day of the year.
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
local Latitude, Longitude=self:GetLLDDM()
local Tdiff=UTILS.GMTToLocalTimeDifference()
local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
if InSeconds then
return sunset
else
return UTILS.SecondsToClock(sunset, true)
end
end
--- Get todays sun set time.
-- @param #COORDINATE self
-- @param #boolean InSeconds If true, return the sun set time in seconds.
-- @return #string Sunrise time, e.g. "20:41".
function COORDINATE:GetSunset(InSeconds)
-- Get current day of the year.
local DayOfYear=UTILS.GetMissionDayOfYear()
-- Lat and long at this point.
local Latitude, Longitude=self:GetLLDDM()
-- GMT time diff.
local Tdiff=UTILS.GMTToLocalTimeDifference()
-- Sunrise in seconds of the day.
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
local date=UTILS.GetDCSMissionDate()
-- Debug output.
--self:I(string.format("Sun set at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff))
if InSeconds then
return sunrise
else
return UTILS.SecondsToClock(sunrise, true)
end
end
--- Get minutes until the next sun set at this coordinate.
-- @param #COORDINATE self
-- @param OnlyToday If true, only calculate the sun set of today. If sun has already set, the time in negative minutes since sunset is reported.
-- @return #number Minutes to the next sunrise.
function COORDINATE:GetMinutesToSunset(OnlyToday)
-- Seconds of today
local time=UTILS.SecondsOfToday()
-- Next Sunset in seconds.
local sunset=nil
-- Time to sunrise.
local delta=nil
if OnlyToday then
---
-- Sunset of today
---
sunset=self:GetSunset(true)
delta=sunset-time
else
---
-- Sunset of tomorrow
---
-- Tomorrows day of the year.
local DayOfYear=UTILS.GetMissionDayOfYear()+1
local Latitude, Longitude=self:GetLLDDM()
local Tdiff=UTILS.GMTToLocalTimeDifference()
sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
delta=sunset+UTILS.SecondsToMidnight()
end
return delta/60
end
--- Return a BR string from a COORDINATE to the COORDINATE.
-- @param #COORDINATE self
@@ -2036,6 +2395,14 @@ do -- COORDINATE
return ""
end
--- Get Latitude and Longitude in Degrees Decimal Minutes (DDM).
-- @param #COORDINATE self
-- @return #number Latitude in DDM.
-- @return #number Lontitude in DDM.
function COORDINATE:GetLLDDM()
return coord.LOtoLL( self:GetVec3() )
end
--- Provides a Lat Lon string in Degree Minute Second format.
-- @param #COORDINATE self
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.

View File

@@ -429,7 +429,7 @@ BEACON = {
-- @field #number VOR
-- @field #number DME
-- @field #number VOR_DME
-- @field #number TACAN
-- @field #number TACAN TACtical Air Navigation system.
-- @field #number VORTAC
-- @field #number RSBN
-- @field #number BROADCAST_STATION
@@ -440,45 +440,74 @@ BEACON = {
-- @field #number ILS_NEAR_HOMER
-- @field #number ILS_LOCALIZER
-- @field #number ILS_GLIDESLOPE
-- @field #number PRMG_LOCALIZER
-- @field #number PRMG_GLIDESLOPE
-- @field #number ICLS Same as ICLS glideslope.
-- @field #number ICLS_LOCALIZER
-- @field #number ICLS_GLIDESLOPE
-- @field #number NAUTICAL_HOMER
-- @field #number ICLS
BEACON.Type={
NULL = 0,
VOR = 1,
DME = 2,
VOR_DME = 3,
TACAN = 4,
VORTAC = 5,
RSBN = 32,
BROADCAST_STATION = 1024,
HOMER = 8,
AIRPORT_HOMER = 4104,
NULL = 0,
VOR = 1,
DME = 2,
VOR_DME = 3,
TACAN = 4,
VORTAC = 5,
RSBN = 128,
BROADCAST_STATION = 1024,
HOMER = 8,
AIRPORT_HOMER = 4104,
AIRPORT_HOMER_WITH_MARKER = 4136,
ILS_FAR_HOMER = 16408,
ILS_NEAR_HOMER = 16456,
ILS_LOCALIZER = 16640,
ILS_GLIDESLOPE = 16896,
NAUTICAL_HOMER = 32776,
ICLS = 131584,
ILS_FAR_HOMER = 16408,
ILS_NEAR_HOMER = 16424,
ILS_LOCALIZER = 16640,
ILS_GLIDESLOPE = 16896,
PRMG_LOCALIZER = 33024,
PRMG_GLIDESLOPE = 33280,
ICLS = 131584, --leaving this in here but it is the same as ICLS_GLIDESLOPE
ICLS_LOCALIZER = 131328,
ICLS_GLIDESLOPE = 131584,
NAUTICAL_HOMER = 65536,
}
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
-- @type BEACON.System
-- @field #number PAR_10
-- @field #number RSBN_5
-- @field #number TACAN
-- @field #number TACAN_TANKER
-- @field #number ILS_LOCALIZER (This is the one to be used for AA TACAN Tanker!)
-- @field #number ILS_GLIDESLOPE
-- @field #number BROADCAST_STATION
-- @field #number PAR_10 ?
-- @field #number RSBN_5 Russian VOR/DME system.
-- @field #number TACAN TACtical Air Navigation system on ground.
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
-- @field #number VOR Very High Frequency Omni-Directional Range
-- @field #number ILS_LOCALIZER ILS localizer
-- @field #number ILS_GLIDESLOPE ILS glideslope.
-- @field #number PRGM_LOCALIZER PRGM localizer.
-- @field #number PRGM_GLIDESLOPE PRGM glideslope.
-- @field #number BROADCAST_STATION Broadcast station.
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
-- @field #number ICLS_LOCALIZER Carrier landing system.
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
BEACON.System={
PAR_10 = 1,
RSBN_5 = 2,
TACAN = 3,
TACAN_TANKER = 4,
ILS_LOCALIZER = 5,
ILS_GLIDESLOPE = 6,
BROADCAST_STATION = 7,
PAR_10 = 1,
RSBN_5 = 2,
TACAN = 3,
TACAN_TANKER_X = 4,
TACAN_TANKER_Y = 5,
VOR = 6,
ILS_LOCALIZER = 7,
ILS_GLIDESLOPE = 8,
PRMG_LOCALIZER = 9,
PRMG_GLIDESLOPE = 10,
BROADCAST_STATION = 11,
VORTAC = 12,
TACAN_AA_MODE_X = 13,
TACAN_AA_MODE_Y = 14,
VORDME = 15,
ICLS_LOCALIZER = 16,
ICLS_GLIDESLOPE = 17,
}
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc.

View File

@@ -17,7 +17,7 @@
--
-- @type RADIOQUEUE
-- @field #string ClassName Name of the class "RADIOQUEUE".
-- @field #boolean Debug Debug mode. More info.
-- @field #boolean Debugmode Debug mode. More info.
-- @field #string lid ID for dcs.log.
-- @field #number frequency The radio frequency in Hz.
-- @field #number modulation The radio modulation. Either radio.modulation.AM or radio.modulation.FM.
@@ -38,7 +38,7 @@
-- @extends Core.Base#BASE
RADIOQUEUE = {
ClassName = "RADIOQUEUE",
Debug = nil,
Debugmode = nil,
lid = nil,
frequency = nil,
modulation = nil,
@@ -55,7 +55,7 @@ RADIOQUEUE = {
power = nil,
numbers = {},
checking = nil,
schedonce = nil,
schedonce = false,
}
--- Radio queue transmission data.
@@ -375,8 +375,10 @@ function RADIOQUEUE:Broadcast(transmission)
sender:SetCommand(commandTransmit)
-- Debug message.
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
MESSAGE:New(text, 2, "RADIOQUEUE "..self.alias):ToAllIf(self.Debug)
if self.Debugmode then
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
MESSAGE:New(text, 2, "RADIOQUEUE "..self.alias):ToAll()
end
else
@@ -388,10 +390,7 @@ function RADIOQUEUE:Broadcast(transmission)
-- Try to get positon from sender unit/static.
if self.sendername then
local coord=self:_GetRadioSenderCoord()
if coord then
vec3=coord:GetVec3()
end
vec3=self:_GetRadioSenderCoord()
end
-- Try to get fixed positon.
@@ -408,8 +407,10 @@ function RADIOQUEUE:Broadcast(transmission)
trigger.action.radioTransmission(filename, vec3, self.modulation, false, self.frequency, self.power)
-- Debug message.
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
MESSAGE:New(string.format(text, filename, transmission.duration, transmission.subtitle or ""), 5, "RADIOQUEUE "..self.alias):ToAllIf(self.Debug)
if self.Debugmode then
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
MESSAGE:New(string.format(text, filename, transmission.duration, transmission.subtitle or ""), 5, "RADIOQUEUE "..self.alias):ToAll()
end
end
end
@@ -524,7 +525,7 @@ end
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
-- @param #RADIOQUEUE self
-- @return Wrapper.Unit#UNIT Sending aircraft unit or nil if was not setup, is not an aircraft or is not alive.
-- @return Wrapper.Unit#UNIT Sending unit or nil if was not setup, is not an aircraft or ground unit or is not alive.
function RADIOQUEUE:_GetRadioSender()
-- Check if we have a sending aircraft.
@@ -532,11 +533,12 @@ function RADIOQUEUE:_GetRadioSender()
-- Try the general default.
if self.sendername then
-- First try to find a unit
sender=UNIT:FindByName(self.sendername)
-- Check that sender is alive and an aircraft.
if sender and sender:IsAlive() and sender:IsAir() then
if sender and sender:IsAlive() and (sender:IsAir() or sender:IsGround()) then
return sender
end
@@ -547,7 +549,7 @@ end
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
-- @param #RADIOQUEUE self
-- @return Core.Point#COORDINATE Coordinate of the sender unit.
-- @return DCS#Vec3 Vector 3D.
function RADIOQUEUE:_GetRadioSenderCoord()
local vec3=nil
@@ -560,7 +562,7 @@ function RADIOQUEUE:_GetRadioSenderCoord()
-- Check that sender is alive and an aircraft.
if sender and sender:IsAlive() then
return sender:GetCoordinate()
return sender:GetVec3()
end
-- Now try a static.
@@ -568,7 +570,7 @@ function RADIOQUEUE:_GetRadioSenderCoord()
-- Check that sender is alive and an aircraft.
if sender then
return sender:GetCoordinate()
return sender:GetVec3()
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,104 +1,156 @@
--- **Core** - Spawn new statics in your running missions.
--- **Core** - Spawn statics.
--
-- ===
--
-- ## Features:
--
-- * Spawn new statics from a static already defined using the mission editor.
-- * Spawn new statics from a static already defined in the mission editor.
-- * Spawn new statics from a given template.
-- * Spawn new statics from a given type.
-- * Spawn with a custom heading and location.
-- * Spawn within a zone.
-- * Spawn statics linked to units, .e.g on aircraft carriers.
--
-- ===
--
-- # Demo Missions
--
-- ### [SPAWNSTATIC Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPS - Spawning Statics)
--
-- ### [SPAWNSTATIC Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
-- ## [SPAWNSTATIC Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ===
--
-- # YouTube Channel
--
-- ### [SPAWNSTATIC YouTube Channel]()
-- ## [SPAWNSTATIC YouTube Channel]() [No videos yet!]
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
-- ### Contributions: **funkyfranky**
--
-- ===
--
-- @module Core.SpawnStatic
-- @image Core_Spawnstatic.JPG
--- @type SPAWNSTATIC
-- @field #string SpawnTemplatePrefix Name of the template group.
-- @field #number CountryID Country ID.
-- @field #number CoalitionID Coalition ID.
-- @field #number CategoryID Category ID.
-- @field #number SpawnIndex Running number increased with each new Spawn.
-- @field Wrapper.Unit#UNIT InitLinkUnit The unit the static is linked to.
-- @field #number InitOffsetX Link offset X coordinate.
-- @field #number InitOffsetY Link offset Y coordinate.
-- @field #number InitOffsetAngle Link offset angle in degrees.
-- @field #number InitStaticHeading Heading of the static.
-- @field #string InitStaticLivery Livery for aircraft.
-- @field #string InitStaticShape Shape of teh static.
-- @field #string InitStaticType Type of the static.
-- @field #string InitStaticCategory Categrory of the static.
-- @field #string InitStaticName Name of the static.
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
-- @field #boolean InitDead Set static to be dead if true.
-- @field #boolean InitCargo If true, static can act as cargo.
-- @field #number InitCargoMass Mass of cargo in kg.
-- @extends Core.Base#BASE
--- Allows to spawn dynamically new @{Static}s.
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME),
-- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy"
-- these properties to create a new static object and place it at the desired coordinate.
--- Allows to spawn dynamically new @{Static}s into your mission.
--
-- New spawned @{Static}s get **the same name** as the name of the template Static,
-- or gets the given name when a new name is provided at the Spawn method.
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc),
-- and "copy" these properties to create a new static object and place it at the desired coordinate.
--
-- New spawned @{Static}s get **the same name** as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
-- By default, spawned @{Static}s will follow a naming convention at run-time:
--
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**,
-- and _nnn_ is a **counter from 0 to 99999**.
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**, and _nnn_ is a **counter from 0 to 99999**.
--
--
-- ## SPAWNSTATIC construction methods
-- # SPAWNSTATIC Constructors
--
-- Create a new SPAWNSTATIC object with the @{#SPAWNSTATIC.NewFromStatic}():
-- Firstly, we need to create a SPAWNSTATIC object that will be used to spawn new statics into the mission. There are three ways to do this.
--
-- * @{#SPAWNSTATIC.NewFromStatic}(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static.
-- ## Use another Static
--
-- A new SPAWNSTATIC object can be created using another static by the @{#SPAWNSTATIC.NewFromStatic}() function. All parameters such as position, heading, country will be initialized
-- from the static.
--
-- ## From a Template
--
-- A SPAWNSTATIC object can also be created from a template table using the @{#SPAWNSTATIC.NewFromTemplate}(SpawnTemplate, CountryID) function. All parameters are taken from the template.
--
-- ## From a Type
--
-- A very basic method is to create a SPAWNSTATIC object by just giving the type of the static. All parameters must be initialized from the InitXYZ functions described below. Otherwise default values
-- are used. For example, if no spawn coordinate is given, the static will be created at the origin of the map.
--
-- # Setting Parameters
--
-- Parameters such as the spawn position, heading, country etc. can be set via :Init*XYZ* functions. Note that these functions must be given before the actual spawn command!
--
-- * @{#SPAWNSTATIC.InitCoordinate}(Coordinate) Sets the coordinate where the static is spawned. Statics are always spawnd on the ground.
-- * @{#SPAWNSTATIC.InitHeading}(Heading) sets the orientation of the static.
-- * @{#SPAWNSTATIC.InitLivery}(LiveryName) sets the livery of the static. Not all statics support this.
-- * @{#SPAWNSTATIC.InitType}(StaticType) sets the type of the static.
-- * @{#SPAWNSTATIC.InitShape}(StaticType) sets the shape of the static. Not all statics have this parameter.
-- * @{#SPAWNSTATIC.InitNamePrefix}(NamePrefix) sets the name prefix of the spawned statics.
-- * @{#SPAWNSTATIC.InitCountry}(CountryID) sets the country and therefore the coalition of the spawned statics.
-- * @{#SPAWNSTATIC.InitLinkToUnit}(Unit, OffsetX, OffsetY, OffsetAngle) links the static to a unit, e.g. to an aircraft carrier.
--
-- ## **Spawn** methods
-- # Spawning the Statics
--
-- Groups can be spawned at different times and methods:
-- Once the SPAWNSTATIC object is created and parameters are initialized, the spawn command can be given. There are different methods where some can be used to directly set parameters
-- such as position and heading.
--
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(): Spawn a new group from a POINT_VEC2 coordinate.
-- (The group will be spawned at land height ).
-- * @{#SPAWNSTATIC.SpawnFromZone}(): Spawn a new group in a @{Zone}.
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Zone}. Optionally, heading and name can be given. The name **must be unique**!
--
-- @field #SPAWNSTATIC SPAWNSTATIC
--
SPAWNSTATIC = {
ClassName = "SPAWNSTATIC",
ClassName = "SPAWNSTATIC",
SpawnIndex = 0,
}
--- Static template table data.
-- @type SPAWNSTATIC.TemplateData
-- @field #string name Name of the static.
-- @field #string type Type of the static.
-- @field #string category Category of the static.
-- @field #number x X-coordinate of the static.
-- @field #number y Y-coordinate of teh static.
-- @field #number heading Heading in rad.
-- @field #boolean dead Static is dead if true.
-- @field #string livery_id Livery name.
-- @field #number unitId Unit ID.
-- @field #number groupId Group ID.
-- @field #table offsets Offset parameters when linked to a unit.
-- @field #number mass Cargo mass in kg.
-- @field #boolean canCargo Static can be a cargo.
--- @type SPAWNSTATIC.SpawnZoneTable
-- @list <Core.Zone#ZONE_BASE> SpawnZone
--- Creates the main object to spawn a @{Static} defined in the ME.
--- Creates the main object to spawn a @{Static} defined in the mission editor (ME).
-- @param #SPAWNSTATIC self
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.
-- @param DCS#country.id SpawnCountryID The ID of the country.
-- @param DCS#coalition.side SpawnCoalitionID The ID of the coalition.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID, SpawnCoalitionID )
-- @param #string SpawnTemplateName Name of the static object in the ME. Each new static will have the name starting with this prefix.
-- @param DCS#country.id SpawnCountryID (Optional) The ID of the country.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTemplatePrefix } )
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( SpawnTemplatePrefix )
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.CountryID = SpawnCountryID or CountryID
self.CategoryID = CategoryID
self.CoalitionID = SpawnCoalitionID or CoalitionID
self.SpawnIndex = 0
self.SpawnTemplatePrefix = SpawnTemplateName
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
self.CountryID = SpawnCountryID or CountryID
self.CategoryID = CategoryID
self.CoalitionID = CoalitionID
self.SpawnIndex = 0
else
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
end
self:SetEventPriority( 5 )
@@ -106,206 +158,284 @@ function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID, SpawnCo
return self
end
--- Creates the main object to spawn a @{Static} based on a type name.
--- Creates the main object to spawn a @{Static} given a template table.
-- @param #SPAWNSTATIC self
-- @param #string SpawnTypeName is the name of the type.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, SpawnCountryID, SpawnCoalitionID )
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTypeName } )
self.SpawnTypeName = SpawnTypeName
self.CountryID = SpawnCountryID
self.CoalitionID = SpawnCoalitionID
self.SpawnIndex = 0
-- @param #table SpawnTemplate Template used for spawning.
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID)
self:SetEventPriority( 5 )
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self.TemplateStaticUnit = UTILS.DeepCopy(SpawnTemplate)
self.SpawnTemplatePrefix = SpawnTemplate.name
self.CountryID = CountryID or country.id.USA
return self
end
--- Creates the main object to spawn a @{Static} from a given type.
-- NOTE that you have to init many other parameters as spawn coordinate etc.
-- @param #SPAWNSTATIC self
-- @param #string StaticType Type of the static.
-- @param #string StaticCategory Category of the static, e.g. "Planes".
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:NewFromType(StaticType, StaticCategory, CountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self.InitStaticType=StaticType
self.InitStaticCategory=StaticCategory
self.CountryID=CountryID or country.id.USA
self.SpawnTemplatePrefix=self.InitStaticType
self.InitStaticCoordinate=COORDINATE:New(0, 0, 0)
self.InitStaticHeading=0
return self
end
--- Creates a new @{Static} at the original position.
--- Initialize heading of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3
self:F( { Heading, NewName } )
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local StaticUnitTemplate = StaticTemplate.units[1]
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
self.SpawnIndex = self.SpawnIndex + 1
return _DATABASE:FindStatic(Static:getName())
end
return nil
-- @param Core.Point#COORDINATE Coordinate Position where the static is spawned.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCoordinate(Coordinate)
self.InitStaticCoordinate=Coordinate
return self
end
--- Initialize heading of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #number Heading The heading in degrees.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitHeading(Heading)
self.InitStaticHeading=Heading
return self
end
--- Initialize livery of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #string LiveryName Name of the livery to use.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitLivery(LiveryName)
self.InitStaticLivery=LiveryName
return self
end
--- Initialize type of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #string StaticType Type of the static, e.g. "FA-18C_hornet".
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitType(StaticType)
self.InitStaticType=StaticType
return self
end
--- Initialize shape of the spawned static. Required by some but not all statics.
-- @param #SPAWNSTATIC self
-- @param #string StaticShape Shape of the static, e.g. "carrier_tech_USA".
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitShape(StaticShape)
self.InitStaticShape=StaticShape
return self
end
--- Initialize cargo mass.
-- @param #SPAWNSTATIC self
-- @param #number Mass Mass of the cargo in kg.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargoMass(Mass)
self.InitCargoMass=Mass
return self
end
--- Initialize as cargo.
-- @param #SPAWNSTATIC self
-- @param #boolean IsCargo If true, this static can act as cargo.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargo(IsCargo)
self.InitCargo=IsCargo
return self
end
--- Initialize country of the spawned static. This determines the category.
-- @param #SPAWNSTATIC self
-- @param #string CountryID The country ID, e.g. country.id.USA.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCountry(CountryID)
self.CountryID=CountryID
return self
end
--- Initialize name prefix statics get. This will be appended by "#0001", "#0002" etc.
-- @param #SPAWNSTATIC self
-- @param #string NamePrefix Name prefix of statics spawned. Will append #0001, etc to the name.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitNamePrefix(NamePrefix)
self.SpawnTemplatePrefix=NamePrefix
return self
end
--- Init link to a unit.
-- @param #SPAWNSTATIC self
-- @param Wrapper.Unit#UNIT Unit The unit to which the static is linked.
-- @param #number OffsetX Offset in X.
-- @param #number OffsetY Offset in Y.
-- @param #number OffsetAngle Offset angle in degrees.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
self.InitLinkUnit=Unit
self.InitOffsetX=OffsetX or 0
self.InitOffsetY=OffsetY or 0
self.InitOffsetAngle=OffsetAngle or 0
return self
end
--- Spawn a new STATIC object.
-- @param #SPAWNSTATIC self
-- @param #number Heading (Optional) The heading of the static, which is a number in degrees from 0 to 360. Default is the heading of the template.
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:Spawn(Heading, NewName)
if Heading then
self.InitStaticHeading=Heading
end
if NewName then
self.InitStaticName=NewName
end
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
end
--- Creates a new @{Static} from a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
self:F( { PointVec2, Heading, NewName } )
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local StaticUnitTemplate = StaticTemplate.units[1]
StaticUnitTemplate.x = PointVec2.x
StaticUnitTemplate.y = PointVec2.z
StaticTemplate.route = nil
StaticTemplate.groupId = nil
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticUnitTemplate.name = StaticTemplate.name
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
self:F({StaticTemplate = StaticTemplate})
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:SpawnFromPointVec2(PointVec2, Heading, NewName)
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
self.SpawnIndex = self.SpawnIndex + 1
return _DATABASE:FindStatic(Static:getName())
end
local vec2={x=PointVec2:GetX(), y=PointVec2:GetY()}
local Coordinate=COORDINATE:NewFromVec2(vec2)
return nil
return self:SpawnFromCoordinate(Coordinate, Heading, NewName)
end
--- Creates a new @{Static} from a COORDINATE.
-- @param #SPAWNSTATIC self
-- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static.
-- @param #number Heading (Optional) Heading The heading of the static, which is a number in degrees from 0 to 360. Default is 0 degrees.
-- @param #number Heading (Optional) Heading The heading of the static in degrees. Default is 0 degrees.
-- @param #string NewName (Optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName) --R2.4
self:F( { PointVec2, Heading, NewName } )
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
Heading=Heading or 0
local StaticUnitTemplate = StaticTemplate.units[1]
StaticUnitTemplate.x = Coordinate.x
StaticUnitTemplate.y = Coordinate.z
StaticUnitTemplate.alt = Coordinate.y
StaticTemplate.route = nil
StaticTemplate.groupId = nil
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticUnitTemplate.name = StaticTemplate.name
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
self:F({StaticTemplate = StaticTemplate})
-- @return Wrapper.Static#STATIC The spawned STATIC object.
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName)
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
self.SpawnIndex = self.SpawnIndex + 1
return _DATABASE:FindStatic(Static:getName())
-- Set up coordinate.
self.InitStaticCoordinate=Coordinate
if Heading then
self.InitStaticHeading=Heading
end
return nil
end
--- Respawns the original @{Static}.
-- @param #SPAWNSTATIC self
-- @param #number delay Delay before respawn in seconds.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:ReSpawn(delay)
if delay and delay>0 then
self:ScheduleOnce(delay, SPAWNSTATIC.ReSpawn, self)
else
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local StaticUnitTemplate = StaticTemplate.units[1]
StaticTemplate.route = nil
StaticTemplate.groupId = nil
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
return _DATABASE:FindStatic(Static:getName())
end
return nil
if NewName then
self.InitStaticName=NewName
end
return self
end
--- Creates the original @{Static} at a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#COORDINATE Coordinate The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading )
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local StaticUnitTemplate = StaticTemplate.units[1]
StaticUnitTemplate.x = Coordinate.x
StaticUnitTemplate.y = Coordinate.z
StaticUnitTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
return _DATABASE:FindStatic(Static:getName())
end
return nil
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
end
--- Creates a new @{Static} from a @{Zone}.
-- @param #SPAWNSTATIC self
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string NewName (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1
self:F( { Zone, Heading, NewName } )
-- @param #number Heading (Optional)The heading of the static in degrees. Default is the heading of the template.
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
-- Spawn the new static at the center of the zone.
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
return Static
end
--- Spawns a new static using a given template. Additionally, the country ID needs to be specified, which also determines the coalition of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #SPAWNSTATIC.TemplateData Template Spawn unit template.
-- @param #number CountryID The country ID.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template=Template or {}
local CountryID=CountryID or self.CountryID
if self.InitStaticType then
Template.type=self.InitStaticType
end
if self.InitStaticCategory then
Template.category=self.InitStaticCategory
end
if self.InitStaticCoordinate then
Template.x = self.InitStaticCoordinate.x
Template.y = self.InitStaticCoordinate.z
Template.alt = self.InitStaticCoordinate.y
end
if self.InitStaticHeading then
Template.heading = math.rad(self.InitStaticHeading)
end
if self.InitStaticShape then
Template.shape_name=self.InitStaticShape
end
if self.InitStaticLivery then
Template.livery_id=self.InitStaticLivery
end
if self.InitDead~=nil then
Template.dead=self.InitDead
end
if self.InitCargo~=nil then
Template.isCargo=self.InitCargo
end
if self.InitLinkUnit then
Template.linkUnit=self.InitLinkUnit:GetID()
Template.linkOffset=true
Template.offsets={}
Template.offsets.y=self.InitOffsetY
Template.offsets.x=self.InitOffsetX
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
end
-- Increase spawn index counter.
self.SpawnIndex = self.SpawnIndex + 1
-- Name of the spawned static.
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
-- Register the new static.
--_DATABASE:_RegisterStaticTemplate(Template, self.CoalitionID, self.CategoryID, CountryID)
_DATABASE:AddStatic(Template.name)
-- Debug output.
self:T(Template)
-- Add static to the game.
local Static=coalition.addStaticObject(CountryID, Template)
return _DATABASE:FindStatic(Static:getName())
end

View File

@@ -341,7 +341,7 @@ do
-- @return #SPOT
function SPOT:onafterLaseOff( From, Event, To )
self:F( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
self:F( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
self.Lasing = false

View File

@@ -0,0 +1,274 @@
--- **Core** - Timer scheduler.
--
-- **Main Features:**
--
-- * Delay function calls
-- * Easy set up and little overhead
-- * Set start, stop and time interval
-- * Define max function calls
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Core.Timer
-- @image CORE_Timer.png
--- TIMER class.
-- @type TIMER
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #number tid Timer ID returned by the DCS API function.
-- @field #function func Timer function.
-- @field #table para Parameters passed to the timer function.
-- @field #number Tstart Relative start time in seconds.
-- @field #number Tstop Relative stop time in seconds.
-- @field #number dT Time interval between function calls in seconds.
-- @field #number ncalls Counter of function calls.
-- @field #number ncallsMax Max number of function calls. If reached, timer is stopped.
-- @extends Core.Base#BASE
--- *Better three hours too soon than a minute too late.* William Shakespeare
--
-- ===
--
-- ![Banner Image](..\Presentations\Timer\TIMER_Main.jpg)
--
-- # The TIMER Concept
--
-- The TIMER class is the little sister of the SCHEDULER class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases.
--
-- It provides an easy interface to the DCS [timer.scheduleFunction](https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction).
--
-- # Construction
--
-- A new TIMER is created by the @{#TIMER.New}(*func*, *...*) function
--
-- local mytimer=TIMER:New(myfunction, a, b)
--
-- The first parameter *func* is the function that is called followed by the necessary comma separeted parameters that are passed to that function.
--
-- ## Starting the Timer
--
-- The timer is started by the @{#TIMER.Start}(*Tstart*, *dT*, *Duration*) function
--
-- mytimer:Start(5, 1, 20)
--
-- where
--
-- * *Tstart* is the relative start time in seconds. In the example, the first function call happens after 5 sec.
-- * *dT* is the time interval between function calls in seconds. Above, the function is called once per second.
-- * *Duration* is the duration in seconds after which the timer is stopped. This is relative to the start time. Here, the timer will run for 20 seconds.
--
-- Note that
--
-- * if *Tstart* is not specified (*nil*), the first function call happens immediately.
-- * if *dT* is not specified (*nil*), the function is called only once.
-- * if *Duration* is not specified (*nil*), the timer runs forever or until stopped manually or until the max function calls are reached (see below).
--
-- For example,
--
-- mytimer:Start(3) -- Will call the function once after 3 seconds.
-- mytimer:Start(nil, 0.5) -- Will call right now and then every 0.5 sec until all eternaty.
-- mytimer:Start(nil, 2.0, 20) -- Will call right now and then every 2.0 sec for 20 sec.
-- mytimer:Start(1.0, nil, 10) -- Does not make sense as the function is only called once anyway.
--
-- ## Stopping the Timer
--
-- The timer can be stopped manually by the @{#TIMER.Start}(*Delay*) function
--
-- mytimer:Stop()
--
-- If the optional paramter *Delay* is specified, the timer is stopped after *delay* seconds.
--
-- ## Limit Function Calls
--
-- The timer can be stopped after a certain amount of function calles with the @{#TIMER.SetMaxFunctionCalls}(*Nmax*) function
--
-- mytimer:SetMaxFunctionCalls(20)
--
-- where *Nmax* is the number of calls after which the timer is stopped, here 20.
--
-- For example,
--
-- mytimer:SetMaxFunctionCalls(66):Start(1.0, 0.1)
--
-- will start the timer after one second and call the function every 0.1 seconds. Once the function has been called 66 times, the timer is stopped.
--
--
-- @field #TIMER
TIMER = {
ClassName = "TIMER",
lid = nil,
}
--- Timer ID.
_TIMERID=0
--- Timer data base.
--_TIMERDB={}
--- TIMER class version.
-- @field #string version
TIMER.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot.
-- TODO: Write docs.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new TIMER object.
-- @param #TIMER self
-- @param #function Function The function to call.
-- @param ... Parameters passed to the function if any.
-- @return #TIMER self
function TIMER:New(Function, ...)
-- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) --#TIMER
-- Function to call.
self.func=Function
-- Function arguments.
self.para=arg or {}
-- Number of function calls.
self.ncalls=0
-- Increase counter
_TIMERID=_TIMERID+1
-- Set UID.
self.uid=_TIMERID
-- Log id.
self.lid=string.format("TIMER UID=%d | ", self.uid)
-- Add to DB.
--_TIMERDB[self.uid]=self
return self
end
--- Create a new TIMER object.
-- @param #TIMER self
-- @param #number Tstart Relative start time in seconds.
-- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once.
-- @param #number Duration Time in seconds for how long the timer is running. If not specified `nil`, the timer runs forever or until stopped manually by the `TIMER:Stop()` function.
-- @return #TIMER self
function TIMER:Start(Tstart, dT, Duration)
-- Current time.
local Tnow=timer.getTime()
-- Start time in sec.
self.Tstart=Tstart and Tnow+Tstart or Tnow+0.001 -- one millisecond delay if Tstart=nil
-- Set time interval.
self.dT=dT
-- Stop time.
if Duration then
self.Tstop=self.Tstart+Duration
end
-- Call DCS timer function.
self.tid=timer.scheduleFunction(self._Function, self, self.Tstart)
-- Set log id.
self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid)
-- Debug info.
self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop)))
return self
end
--- Stop the timer by removing the timer function.
-- @param #TIMER self
-- @param #number Delay (Optional) Delay in seconds, before the timer is stopped.
-- @return #TIMER self
function TIMER:Stop(Delay)
if Delay and Delay>0 then
self.Tstop=timer.getTime()+Delay
else
if self.tid then
-- Remove timer function.
self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls))
timer.removeFunction(self.tid)
-- Remove DB entry.
--_TIMERDB[self.uid]=nil
end
end
return self
end
--- Set max number of function calls. When the function has been called this many times, the TIMER is stopped.
-- @param #TIMER self
-- @param #number Nmax Set number of max function calls.
-- @return #TIMER self
function TIMER:SetMaxFunctionCalls(Nmax)
self.ncallsMax=Nmax
return self
end
--- Call timer function.
-- @param #TIMER self
-- @param #number time DCS model time in seconds.
-- @return #number Time when the function is called again or `nil` if the timer is stopped.
function TIMER:_Function(time)
-- Call function.
self.func(unpack(self.para))
-- Increase number of calls.
self.ncalls=self.ncalls+1
-- Next time.
local Tnext=self.dT and time+self.dT or nil
-- Check if we stop the timer.
local stopme=false
if Tnext==nil then
-- No next time.
self:T(self.lid..string.format("No next time as dT=nil ==> Stopping timer after %d function calls", self.ncalls))
stopme=true
elseif self.Tstop and Tnext>self.Tstop then
-- Stop time passed.
self:T(self.lid..string.format("Stop time passed %.3f > %.3f ==> Stopping timer after %d function calls", Tnext, self.Tstop, self.ncalls))
stopme=true
elseif self.ncallsMax and self.ncalls>=self.ncallsMax then
-- Number of max function calls reached.
self:T(self.lid..string.format("Max function calls Nmax=%d reached ==> Stopping timer after %d function calls", self.ncallsMax, self.ncalls))
stopme=true
end
if stopme then
-- Remove timer function.
self:Stop()
return nil
else
-- Call again in Tnext seconds.
return Tnext
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -95,6 +95,11 @@ do -- world
--- Returns a table of mark panels indexed numerically that are present within the mission. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_getMarkPanels)
-- @function [parent=#world] getMarkPanels
-- @return #table Table of marks.
--- Returns a table of DCS airbase objects.
-- @function [parent=#world] getAirbases
-- @param #number coalitionId The coalition side number ID. Default is all airbases are returned.
-- @return #table Table of DCS airbase objects.
end -- world
@@ -360,7 +365,7 @@ do -- Types
--- Time is given in seconds.
-- @type Time
-- @extends #number
-- @extends #number Time in seconds.
--- Model time is the time that drives the simulation. Model time may be stopped, accelerated and decelerated relative real time.
-- @type ModelTime
@@ -368,20 +373,20 @@ do -- Types
--- Mission time is a model time plus time of the mission start.
-- @type MissionTime
-- @extends #number
-- @extends #number Time in seconds.
--- Distance is given in meters.
-- @type Distance
-- @extends #number
-- @extends #number Distance in meters.
--- Angle is given in radians.
-- @type Angle
-- @extends #number
-- @extends #number Angle in radians.
--- Azimuth is an angle of rotation around world axis y counter-clockwise.
-- @type Azimuth
-- @extends #number
-- @extends #number Angle in radians.
--- Mass is given in kilograms.
-- @type Mass
@@ -401,15 +406,15 @@ do -- Types
--- Position is a composite structure. It consists of both coordinate vector and orientation matrix. Position3 (also known as "Pos3" for short) is a table that has following format:
-- @type Position3
-- @field #Vec3 p
-- @field #Vec3 x
-- @field #Vec3 y
-- @field #Vec3 z
-- @field #Vec3 p 3D position vector.
-- @field #Vec3 x Orientation component of vector pointing East.
-- @field #Vec3 y Orientation component of vector pointing up.
-- @field #Vec3 z Orientation component of vector pointing North.
--- 3-dimensional box.
-- @type Box3
-- @field #Vec3 min
-- @field #Vec3 max
-- @field #Vec3 min Min.
-- @field #Vec3 max Max
--- Each object belongs to a type. Object type is a named couple of properties those independent of mission and common for all units of the same type. Name of unit type is a string. Samples of unit type: "Su-27", "KAMAZ" and "M2 Bradley".
-- @type TypeName
@@ -514,7 +519,7 @@ do -- Object
--- Returns object coordinates for current time.
-- @function [parent=#Object] getPoint
-- @param #Object self
-- @return #Vec3
-- @return #Vec3 3D position vector with x,y,z components.
--- Returns object position for current time.
-- @function [parent=#Object] getPosition
@@ -524,7 +529,7 @@ do -- Object
--- Returns the unit's velocity vector.
-- @function [parent=#Object] getVelocity
-- @param #Object self
-- @return #Vec3
-- @return #Vec3 3D velocity vector.
--- Returns true if the unit is in air.
-- @function [parent=#Object] inAir

View File

@@ -693,7 +693,7 @@ ARTY.db={
--- Arty script version.
-- @field #string version
ARTY.version="1.1.7"
ARTY.version="1.1.8"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2770,7 +2770,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
self:I(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, ntot, nshells, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles))
self:T(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, ntot, nshells, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles))
if self.Controllable and self.Controllable:IsAlive() then
@@ -4814,7 +4814,8 @@ function ARTY:_CheckShootingStarted()
end
-- Check if we waited long enough and no shot was fired.
if dt > self.WaitForShotTime and self.Nshots==0 then
--if dt > self.WaitForShotTime and self.Nshots==0 then
if dt > self.WaitForShotTime and (self.Nshots==0 or self.currentTarget.nshells >= self.Nshots) then --https://github.com/FlightControl-Master/MOOSE/issues/1356
-- Debug info.
self:T(self.lid..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.groupname, self.WaitForShotTime, name))

View File

@@ -93,6 +93,8 @@
-- @field Core.RadioQueue#RADIOQUEUE instructor Instructor radio queue.
-- @field #number rangecontrolfreq Frequency on which the range control transmitts.
-- @field Core.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue.
-- @field #string rangecontrolrelayname Name of relay unit.
-- @field #string instructorrelayname Name of relay unit.
-- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
-- @extends Core.Fsm#FSM
@@ -516,7 +518,7 @@ RANGE.MenuF10Root=nil
--- Range script version.
-- @field #string version
RANGE.version="2.2.2"
RANGE.version="2.2.3"
--TODO list:
--TODO: Verbosity level for messages.
@@ -770,6 +772,7 @@ function RANGE:onafterStart()
-- Set location where the messages are transmitted from.
self.rangecontrol:SetSenderCoordinate(self.location)
self.rangecontrol:SetSenderUnitName(self.rangecontrolrelayname)
-- Start range control radio queue.
self.rangecontrol:Start(1, 0.1)
@@ -794,6 +797,7 @@ function RANGE:onafterStart()
-- Set location where the messages are transmitted from.
self.instructor:SetSenderCoordinate(self.location)
self.instructor:SetSenderUnitName(self.instructorrelayname)
-- Start instructor radio queue.
self.instructor:Start(1, 0.1)
@@ -1067,18 +1071,22 @@ end
--- Enable range control and set frequency.
-- @param #RANGE self
-- @param #number frequency Frequency in MHz. Default 256 MHz.
-- @param #string relayunitname Name of the unit used for transmission.
-- @return #RANGE self
function RANGE:SetRangeControl(frequency)
function RANGE:SetRangeControl(frequency, relayunitname)
self.rangecontrolfreq=frequency or 256
self.rangecontrolrelayname=relayunitname
return self
end
--- Enable instructor radio and set frequency.
-- @param #RANGE self
-- @param #number frequency Frequency in MHz. Default 305 MHz.
-- @param #string relayunitname Name of the unit used for transmission.
-- @return #RANGE self
function RANGE:SetInstructorRadio(frequency)
function RANGE:SetInstructorRadio(frequency, relayunitname)
self.instructorfreq=frequency or 305
self.instructorrelayname=relayunitname
return self
end
@@ -1908,8 +1916,35 @@ end
-- @param #string To To state.
function RANGE:onafterStatus(From, Event, To)
local fsmstate=self:GetState()
local text=string.format("Range status: %s", fsmstate)
if self.instructor then
local alive="N/A"
if self.instructorrelayname then
local relay=UNIT:FindByName(self.instructorrelayname)
if relay then
alive=tostring(relay:IsAlive())
end
end
text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)", self.instructorfreq, tostring(self.instructorrelayname), alive)
end
if self.rangecontrol then
local alive="N/A"
if self.rangecontrolrelayname then
local relay=UNIT:FindByName(self.rangecontrolrelayname)
if relay then
alive=tostring(relay:IsAlive())
end
end
text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring(self.rangecontrolrelayname), alive)
end
-- Check range status.
self:I(self.id..string.format("Range status: %s", self:GetState()))
self:I(self.id..text)
-- Check player status.
self:_CheckPlayers()

View File

@@ -7025,7 +7025,7 @@ function WAREHOUSE:_CheckRequestNow(request)
-- If no transport is requested, assets need to be mobile unless it is a self request.
local onlymobile=false
if type(request.transport)=="number" and request.ntransport==0 and not request.toself then
if type(request.ntransport)=="number" and request.ntransport==0 and not request.toself then
onlymobile=true
end

View File

@@ -1,6 +1,7 @@
__Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
@@ -24,6 +25,7 @@ __Moose.Include( 'Scripts/Moose/Core/RadioQueue.lua' )
__Moose.Include( 'Scripts/Moose/Core/RadioSpeech.lua' )
__Moose.Include( 'Scripts/Moose/Core/Spawn.lua' )
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
@@ -37,6 +39,7 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Client.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )

View File

@@ -67,8 +67,10 @@
-- @field #number subduration Duration how long subtitles are displayed in seconds.
-- @field #boolean metric If true, use metric units. If false, use imperial (default).
-- @field #boolean PmmHg If true, give pressure in millimeters of Mercury. Default is inHg for imperial and hecto Pascal (=mili Bars) for metric units.
-- @field #boolean qnhonly If true, suppresses reporting QFE. Default is to report both QNH and QFE.
-- @field #boolean TDegF If true, give temperature in degrees Fahrenheit. Default is in degrees Celsius independent of chosen unit system.
-- @field #number zuludiff Time difference local vs. zulu in hours.
-- @field #boolean zulutimeonly If true, suppresses report of local time, sunrise, and sunset.
-- @field #number magvar Magnetic declination/variation at the airport in degrees.
-- @field #table ils Table of ILS frequencies (can be runway specific).
-- @field #table ndbinner Table of inner NDB frequencies (can be runway specific).
@@ -302,8 +304,10 @@ ATIS = {
subduration = nil,
metric = nil,
PmmHg = nil,
qnhonly = false,
TDegF = nil,
zuludiff = nil,
zulutimeonly = false,
magvar = nil,
ils = {},
ndbinner = {},
@@ -362,11 +366,32 @@ ATIS.Alphabet = {
-- @field #number Nevada +12° (East).
-- @field #number Normandy -10° (West).
-- @field #number PersianGulf +2° (East).
-- @field #number TheChannel -10° (West).
-- @field #number Syria +5° (East).
ATIS.RunwayM2T={
Caucasus=0,
Nevada=12,
Normany=-10,
Normandy=-10,
PersianGulf=2,
TheChannel=-10,
Syria=5,
}
--- Whether ICAO phraseology is used for ATIS broadcasts.
-- @type ATIS.ICAOPhraseology
-- @field #boolean Caucasus true.
-- @field #boolean Nevada false.
-- @field #boolean Normandy true.
-- @field #boolean PersianGulf true.
-- @field #boolean TheChannel true.
-- @field #boolean Syria true.
ATIS.ICAOPhraseology={
Caucasus=true,
Nevada=false,
Normandy=true,
PersianGulf=true,
TheChannel=true,
Syria=true,
}
--- Nav point data.
@@ -415,6 +440,7 @@ ATIS.RunwayM2T={
-- @field #ATIS.Soundfile MegaHertz
-- @field #ATIS.Soundfile Meters
-- @field #ATIS.Soundfile MetersPerSecond
-- @field #ATIS.Soundfile Miles
-- @field #ATIS.Soundfile MillimetersOfMercury
-- @field #ATIS.Soundfile N0
-- @field #ATIS.Soundfile N1
@@ -434,6 +460,8 @@ ATIS.RunwayM2T={
-- @field #ATIS.Soundfile Right
-- @field #ATIS.Soundfile Snow
-- @field #ATIS.Soundfile SnowStorm
-- @field #ATIS.Soundfile SunriseAt
-- @field #ATIS.Soundfile SunsetAt
-- @field #ATIS.Soundfile Temperature
-- @field #ATIS.Soundfile Thousand
-- @field #ATIS.Soundfile ThunderStorm
@@ -485,6 +513,7 @@ ATIS.Sound = {
MegaHertz={filename="MegaHertz.ogg", duration=0.87},
Meters={filename="Meters.ogg", duration=0.59},
MetersPerSecond={filename="MetersPerSecond.ogg", duration=1.14},
Miles={filename="Miles.ogg", duration=0.60},
MillimetersOfMercury={filename="MillimetersOfMercury.ogg", duration=1.53},
Minus={filename="Minus.ogg", duration=0.64},
N0={filename="N-0.ogg", duration=0.55},
@@ -505,6 +534,9 @@ ATIS.Sound = {
Right={filename="Right.ogg", duration=0.44},
Snow={filename="Snow.ogg", duration=0.48},
SnowStorm={filename="SnowStorm.ogg", duration=0.82},
StatuteMiles={filename="StatuteMiles.ogg", duration=1.15},
SunriseAt={filename="SunriseAt.ogg", duration=0.92},
SunsetAt={filename="SunsetAt.ogg", duration=0.95},
Temperature={filename="Temperature.ogg", duration=0.64},
Thousand={filename="Thousand.ogg", duration=0.55},
ThunderStorm={filename="ThunderStorm.ogg", duration=0.81},
@@ -522,6 +554,7 @@ ATIS.Sound = {
TACANChannel={filename="TACANChannel.ogg", duration=0.88},
PRMGChannel={filename="PRMGChannel.ogg", duration=1.18},
RSBNChannel={filename="RSBNChannel.ogg", duration=1.14},
Zulu={filename="Zulu.ogg", duration=0.62},
}
@@ -531,7 +564,7 @@ _ATIS={}
--- ATIS class version.
-- @field #string version
ATIS.version="0.7.1"
ATIS.version="0.9.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -599,12 +632,12 @@ function ATIS:New(airbasename, frequency, modulation)
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
self:AddTransition("*", "Status", "*") -- Update status.
self:AddTransition("*", "Broadcast", "*") -- Broadcast ATIS message.
self:AddTransition("*", "CheckQueue", "*") -- Check if radio queue is empty.
self:AddTransition("*", "Report", "*") -- Report ATIS text.
self:AddTransition("*", "Stop", "Stopped") -- Stop.
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
self:AddTransition("*", "Status", "*") -- Update status.
self:AddTransition("*", "Broadcast", "*") -- Broadcast ATIS message.
self:AddTransition("*", "CheckQueue", "*") -- Check if radio queue is empty.
self:AddTransition("*", "Report", "*") -- Report ATIS text.
self:AddTransition("*", "Stop", "Stopped") -- Stop.
------------------------
--- Pseudo Functions ---
@@ -894,6 +927,14 @@ function ATIS:SetAltimeterQNH(switch)
return self
end
--- Suppresses QFE readout. Default is to report both QNH and QFE.
-- @param #ATIS self
-- @return #ATIS self
function ATIS:ReportQNHOnly()
self.qnhonly=true
return self
end
--- Set magnetic declination/variation at the airport.
--
-- Default is per map:
@@ -943,9 +984,10 @@ end
-- Default is per map:
--
-- * Caucasus +4
-- * Nevada -7
-- * Normandy +1
-- * Nevada -8
-- * Normandy 0
-- * Persian Gulf +4
-- * The Channel +2 (should be 0)
--
-- @param #ATIS self
-- @param #number delta Time difference in hours.
@@ -955,6 +997,14 @@ function ATIS:SetZuluTimeDifference(delta)
return self
end
--- Suppresses local time, sunrise, and sunset. Default is to report all these times.
-- @param #ATIS self
-- @return #ATIS self
function ATIS:ReportZuluTimeOnly()
self.zulutimeonly=true
return self
end
--- Add ILS station. Note that this can be runway specific.
-- @param #ATIS self
-- @param #number frequency ILS frequency in MHz.
@@ -1244,8 +1294,6 @@ function ATIS:onafterBroadcast(From, Event, To)
if WINDFROM=="000" then
WINDFROM="360"
end
env.info(string.format("FF WINDFROM = %s", tostring(WINDFROM)))
if self.metric then
WINDSPEED=string.format("%d", windSpeed)
@@ -1267,15 +1315,7 @@ function ATIS:onafterBroadcast(From, Event, To)
-- User specified.
time=time-self.zuludiff*60*60
else
if self.theatre==DCSMAP.Caucasus then
time=time-4*60*60 -- Caucasus UTC+4 hours
elseif self.theatre==DCSMAP.PersianGulf then
time=time-4*60*60 -- Abu Dhabi UTC+4 hours
elseif self.theatre==DCSMAP.NTTR then
time=time+7*60*60 -- Las Vegas UTC-7 hours
elseif self.theatre==DCSMAP.Normandy then
time=time-1*60*60 -- Calais UTC+1 hour
end
time=time-UTILS.GMTToLocalTimeDifference()*60*60
end
local clock=UTILS.SecondsToClock(time)
@@ -1293,6 +1333,18 @@ function ATIS:onafterBroadcast(From, Event, To)
self:T3(string.format("ZULU =%s", tostring(ZULU)))
self:T3(string.format("NATO =%s", tostring(NATO)))
--------------------------
--- Sunrise and Sunset ---
--------------------------
local sunrise=coord:GetSunrise()
sunrise=UTILS.Split(sunrise, ":")
local SUNRISE=string.format("%s%s", sunrise[1], sunrise[2])
local sunset=coord:GetSunset()
sunset=UTILS.Split(sunset, ":")
local SUNSET=string.format("%s%s", sunset[1], sunset[2])
---------------------------------
--- Temperature and Dew Point ---
---------------------------------
@@ -1347,13 +1399,24 @@ function ATIS:onafterBroadcast(From, Event, To)
visibilitymin=dust
end
end
local VISIBILITY=""
-- Visibility in NM.
local VISIBILITY=string.format("%d", UTILS.Round(UTILS.MetersToNM(visibilitymin)))
-- Visibility in km.
if self.metric then
VISIBILITY=string.format("%d", UTILS.Round(visibilitymin/1000))
-- Visibility in km.
local reportedviz=UTILS.Round(visibilitymin/1000)
-- max reported visibility 9999 m
if reportedviz > 10 then
reportedviz=10
end
VISIBILITY=string.format("%d", reportedviz)
else
-- max reported visibility 10 NM
local reportedviz=UTILS.Round(UTILS.MetersToSM(visibilitymin))
if reportedviz > 10 then
reportedviz=10
end
VISIBILITY=string.format("%d", reportedviz)
end
--------------
@@ -1441,60 +1504,65 @@ function ATIS:onafterBroadcast(From, Event, To)
-- Zulu Time
subtitle=string.format("%s Zulu", ZULU)
self.radioqueue:Number2Transmission(ZULU, nil, 0.5)
self:Transmission(ATIS.Sound.TimeZulu, 0.2, subtitle)
self:Transmission(ATIS.Sound.Zulu, 0.2, subtitle)
alltext=alltext..";\n"..subtitle
if not self.zulutimeonly then
-- Sunrise Time
subtitle=string.format("Sunrise at %s local time", SUNRISE)
self:Transmission(ATIS.Sound.SunriseAt, 0.5, subtitle)
self.radioqueue:Number2Transmission(SUNRISE, nil, 0.2)
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
alltext=alltext..";\n"..subtitle
-- Sunset Time
subtitle=string.format("Sunset at %s local time", SUNSET)
self:Transmission(ATIS.Sound.SunsetAt, 0.5, subtitle)
self.radioqueue:Number2Transmission(SUNSET, nil, 0.5)
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
alltext=alltext..";\n"..subtitle
end
-- Wind
if self.metric then
subtitle=string.format("Wind from %s at %s m/s", WINDFROM, WINDSPEED)
else
subtitle=string.format("Wind from %s at %s knots", WINDFROM, WINDSPEED)
end
if turbulence>0 then
subtitle=subtitle..", gusting"
end
local _WIND=subtitle
self:Transmission(ATIS.Sound.WindFrom, 1.0, subtitle)
self.radioqueue:Number2Transmission(WINDFROM)
self:Transmission(ATIS.Sound.At, 0.2)
self.radioqueue:Number2Transmission(WINDSPEED)
if self.metric then
self:Transmission(ATIS.Sound.MetersPerSecond, 0.2)
else
self:Transmission(ATIS.Sound.Knots, 0.2)
end
if turbulence>0 then
self:Transmission(ATIS.Sound.Gusting, 0.2)
end
alltext=alltext..";\n"..subtitle
-- Visibility
if self.metric then
subtitle=string.format("Visibility %s km", VISIBILITY)
else
subtitle=string.format("Visibility %s NM", VISIBILITY)
subtitle=string.format("Visibility %s SM", VISIBILITY)
end
self:Transmission(ATIS.Sound.Visibilty, 1.0, subtitle)
self.radioqueue:Number2Transmission(VISIBILITY)
if self.metric then
self:Transmission(ATIS.Sound.Kilometers, 0.2)
else
self:Transmission(ATIS.Sound.NauticalMiles, 0.2)
self:Transmission(ATIS.Sound.StatuteMiles, 0.2)
end
alltext=alltext..";\n"..subtitle
-- Cloud base
self:Transmission(CloudCover, 1.0, CLOUDSsub)
if CLOUDBASE and static then
-- Base
if self.metric then
subtitle=string.format("Cloudbase %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL)
else
subtitle=string.format("Cloudbase %s, ceiling %s ft", CLOUDBASE, CLOUDCEIL)
end
self:Transmission(ATIS.Sound.CloudBase, 1.0, subtitle)
if tonumber(CLOUDBASE1000)>0 then
self.radioqueue:Number2Transmission(CLOUDBASE1000)
self:Transmission(ATIS.Sound.Thousand, 0.1)
end
if tonumber(CLOUDBASE0100)>0 then
self.radioqueue:Number2Transmission(CLOUDBASE0100)
self:Transmission(ATIS.Sound.Hundred, 0.1)
end
-- Ceiling
self:Transmission(ATIS.Sound.CloudCeiling, 0.5)
if tonumber(CLOUDCEIL1000)>0 then
self.radioqueue:Number2Transmission(CLOUDCEIL1000)
self:Transmission(ATIS.Sound.Thousand, 0.1)
end
if tonumber(CLOUDCEIL0100)>0 then
self.radioqueue:Number2Transmission(CLOUDCEIL0100)
self:Transmission(ATIS.Sound.Hundred, 0.1)
end
if self.metric then
self:Transmission(ATIS.Sound.Meters, 0.1)
else
self:Transmission(ATIS.Sound.Feet, 0.1)
end
end
alltext=alltext..";\n"..subtitle
-- Weather phenomena
local wp=false
local wpsub=""
@@ -1550,37 +1618,42 @@ function ATIS:onafterBroadcast(From, Event, To)
alltext=alltext..";\n"..subtitle
end
-- Altimeter QNH/QFE.
if self.PmmHg then
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
else
-- Cloud base
self:Transmission(CloudCover, 1.0, CLOUDSsub)
if CLOUDBASE and static then
-- Base
if self.metric then
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2])
subtitle=string.format("Cloudbase %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL)
else
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
subtitle=string.format("Cloudbase %s, ceiling %s ft", CLOUDBASE, CLOUDCEIL)
end
self:Transmission(ATIS.Sound.CloudBase, 1.0, subtitle)
if tonumber(CLOUDBASE1000)>0 then
self.radioqueue:Number2Transmission(CLOUDBASE1000)
self:Transmission(ATIS.Sound.Thousand, 0.1)
end
if tonumber(CLOUDBASE0100)>0 then
self.radioqueue:Number2Transmission(CLOUDBASE0100)
self:Transmission(ATIS.Sound.Hundred, 0.1)
end
-- Ceiling
self:Transmission(ATIS.Sound.CloudCeiling, 0.5)
if tonumber(CLOUDCEIL1000)>0 then
self.radioqueue:Number2Transmission(CLOUDCEIL1000)
self:Transmission(ATIS.Sound.Thousand, 0.1)
end
if tonumber(CLOUDCEIL0100)>0 then
self.radioqueue:Number2Transmission(CLOUDCEIL0100)
self:Transmission(ATIS.Sound.Hundred, 0.1)
end
end
local _ALTIMETER=subtitle
self:Transmission(ATIS.Sound.Altimeter, 1.0, subtitle)
self:Transmission(ATIS.Sound.QNH, 0.5)
self.radioqueue:Number2Transmission(QNH[1])
self:Transmission(ATIS.Sound.Decimal, 0.2)
self.radioqueue:Number2Transmission(QNH[2])
self:Transmission(ATIS.Sound.QFE, 0.75)
self.radioqueue:Number2Transmission(QFE[1])
self:Transmission(ATIS.Sound.Decimal, 0.2)
self.radioqueue:Number2Transmission(QFE[2])
if self.PmmHg then
self:Transmission(ATIS.Sound.MillimetersOfMercury, 0.1)
else
if self.metric then
self:Transmission(ATIS.Sound.HectoPascal, 0.1)
self:Transmission(ATIS.Sound.Meters, 0.1)
else
self:Transmission(ATIS.Sound.InchesOfMercury, 0.1)
self:Transmission(ATIS.Sound.Feet, 0.1)
end
end
alltext=alltext..";\n"..subtitle
-- Temperature
if self.TDegF then
if temperature<0 then
@@ -1607,7 +1680,7 @@ function ATIS:onafterBroadcast(From, Event, To)
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
end
alltext=alltext..";\n"..subtitle
-- Dew point
if self.TDegF then
if dewpoint<0 then
@@ -1635,27 +1708,57 @@ function ATIS:onafterBroadcast(From, Event, To)
end
alltext=alltext..";\n"..subtitle
-- Wind
if self.metric then
subtitle=string.format("Wind from %s at %s m/s", WINDFROM, WINDSPEED)
-- Altimeter QNH/QFE.
if self.PmmHg then
if self.qnhonly then
subtitle=string.format("Altimeter %s.%s mmHg", QNH[1], QNH[2])
else
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
end
else
subtitle=string.format("Wind from %s at %s knots", WINDFROM, WINDSPEED)
if self.metric then
if self.qnhonly then
subtitle=string.format("Altimeter %s.%s hPa", QNH[1], QNH[2])
else
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2])
end
else
if self.qnhonly then
subtitle=string.format("Altimeter %s.%s inHg", QNH[1], QNH[2])
else
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
end
end
end
if turbulence>0 then
subtitle=subtitle..", gusting"
local _ALTIMETER=subtitle
self:Transmission(ATIS.Sound.Altimeter, 1.0, subtitle)
if not self.qnhonly then
self:Transmission(ATIS.Sound.QNH, 0.5)
end
local _WIND=subtitle
self:Transmission(ATIS.Sound.WindFrom, 1.0, subtitle)
self.radioqueue:Number2Transmission(WINDFROM)
self:Transmission(ATIS.Sound.At, 0.2)
self.radioqueue:Number2Transmission(WINDSPEED)
if self.metric then
self:Transmission(ATIS.Sound.MetersPerSecond, 0.2)
self.radioqueue:Number2Transmission(QNH[1])
if ATIS.ICAOPhraseology[UTILS.GetDCSMap()] then
self:Transmission(ATIS.Sound.Decimal, 0.2)
end
self.radioqueue:Number2Transmission(QNH[2])
if not self.qnhonly then
self:Transmission(ATIS.Sound.QFE, 0.75)
self.radioqueue:Number2Transmission(QFE[1])
if ATIS.ICAOPhraseology[UTILS.GetDCSMap()] then
self:Transmission(ATIS.Sound.Decimal, 0.2)
end
self.radioqueue:Number2Transmission(QFE[2])
end
if self.PmmHg then
self:Transmission(ATIS.Sound.MillimetersOfMercury, 0.1)
else
self:Transmission(ATIS.Sound.Knots, 0.2)
end
if turbulence>0 then
self:Transmission(ATIS.Sound.Gusting, 0.2)
if self.metric then
self:Transmission(ATIS.Sound.HectoPascal, 0.1)
else
self:Transmission(ATIS.Sound.InchesOfMercury, 0.1)
end
end
alltext=alltext..";\n"..subtitle
@@ -1873,13 +1976,6 @@ function ATIS:onafterBroadcast(From, Event, To)
alltext=alltext..";\n"..subtitle
end
--[[
-- End of Information Alpha, Bravo, ...
subtitle=string.format("End of information %s", NATO)
self:Transmission(ATIS.Sound.EndOfInformation, 0.5, subtitle)
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
--]]
-- Advice on initial...
subtitle=string.format("Advise on initial contact, you have information %s", NATO)

View File

@@ -1273,17 +1273,19 @@ AIRBOSS.AircraftCarrier={
--- Carrier types.
-- @type AIRBOSS.CarrierType
-- @field #string ROOSEVELT USS Theodore Roosevelt (CVN-71)
-- @field #string LINCOLN USS Abraham Lincoln (CVN-72)
-- @field #string WASHINGTON USS George Washington (CVN-73)
-- @field #string ROOSEVELT USS Theodore Roosevelt (CVN-71) [Super Carrier Module]
-- @field #string LINCOLN USS Abraham Lincoln (CVN-72) [Super Carrier Module]
-- @field #string WASHINGTON USS George Washington (CVN-73) [Super Carrier Module]
-- @field #string STENNIS USS John C. Stennis (CVN-74)
-- @field #string VINSON USS Carl Vinson (CVN-70)
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
-- @field #string TARAWA USS Tarawa (LHA-1)
-- @field #string KUZNETSOV Admiral Kuznetsov (CV 1143.5)
AIRBOSS.CarrierType={
ROOSEVELT="CVN_71",
LINCOLN="CVN_72",
WASHINGTON="CVN_73",
TRUMAN="CVN_75",
STENNIS="Stennis",
VINSON="VINSON",
TARAWA="LHA_Tarawa",
@@ -1697,7 +1699,7 @@ AIRBOSS.MenuF10Root=nil
--- Airboss class version.
-- @field #string version
AIRBOSS.version="1.1.3"
AIRBOSS.version="1.1.5"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1936,11 +1938,13 @@ function AIRBOSS:New(carriername, alias)
if self.carriertype==AIRBOSS.CarrierType.STENNIS then
self:_InitStennis()
elseif self.carriertype==AIRBOSS.CarrierType.ROOSEVELT then
self:_InitStennis()
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.LINCOLN then
self:_InitStennis()
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
self:_InitStennis()
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.TRUMAN then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.VINSON then
-- TODO: Carl Vinson parameters.
self:_InitStennis()
@@ -2018,7 +2022,7 @@ function AIRBOSS:New(carriername, alias)
-- Bow
bow:FlareYellow()
-- Runway half width = 10 m.
local r1=stern:Translate(self.carrierparam.rwywidth*0.5, FB+90)
local r2=stern:Translate(self.carrierparam.rwywidth*0.5, FB-90)
@@ -2464,7 +2468,7 @@ function AIRBOSS:AddRecoveryWindow(starttime, stoptime, case, holdingoffset, tur
local Tstart=UTILS.ClockToSeconds(starttime)
-- Set stop time.
local Tstop=UTILS.ClockToSeconds(stoptime or Tstart+90*60)
local Tstop=stoptime and UTILS.ClockToSeconds(stoptime) or Tstart+90*60
-- Consistancy check for timing.
if Tstart>Tstop then
@@ -2624,10 +2628,10 @@ end
--- Set time before carrier turns and recovery window opens.
-- @param #AIRBOSS self
-- @param #number interval Time interval in seconds. Default 600 sec.
-- @param #number interval Time interval in seconds. Default 300 sec.
-- @return #AIRBOSS self
function AIRBOSS:SetRecoveryTurnTime(interval)
self.dTturn=interval or 600
self.dTturn=interval or 300
return self
end
@@ -4094,7 +4098,7 @@ function AIRBOSS:onafterRecoveryUnpause(From, Event, To)
self:T(self.lid..string.format("Unpausing aircraft recovery."))
-- Resume recovery.
self:_MarshalCallRecoveryResume()
self:_MarshalCallResumeRecovery()
end
@@ -4292,7 +4296,36 @@ function AIRBOSS:_InitStennis()
end
--- Init parameters for USS Stennis carrier.
--- Init parameters for Nimitz class super carriers.
-- @param #AIRBOSS self
function AIRBOSS:_InitNimitz()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist =-164
self.carrierparam.deckheight = 20
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength=332.8 -- Wiki says 332.8 meters overall length.
self.carrierparam.totwidthport=45 -- Wiki says 76.8 meters overall beam.
self.carrierparam.totwidthstarboard=35
-- Landing runway.
self.carrierparam.rwyangle = -9
self.carrierparam.rwylength = 250
self.carrierparam.rwywidth = 25
-- Wires.
self.carrierparam.wire1 = 55 -- Distance from stern to first wire.
self.carrierparam.wire2 = 67
self.carrierparam.wire3 = 79
self.carrierparam.wire4 = 92
end
--- Init parameters for LHA-1 Tarawa carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitTarawa()
@@ -7866,7 +7899,7 @@ end
function AIRBOSS:_RemoveUnitFromFlight(unit)
-- Check if unit exists.
if unit then
if unit and unit:IsInstanceOf("UNIT") then
-- Get group.
local group=unit:GetGroup()
@@ -10297,9 +10330,12 @@ function AIRBOSS:_GetSternCoord()
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
-- Tarawa: Translate 8 meters port.
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(8, FB-90)
else
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
-- Stennis: translate 7 meters starboard wrt Final bearing.
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(7, FB+90)
else
-- Nimitz SC: translate 8 meters starboard wrt Final bearing.
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(8.5, FB+90)
end
-- Set altitude.
@@ -14016,10 +14052,10 @@ function AIRBOSS:_GetOnboardNumbers(group, playeronly)
-- Onboard number and unit name.
local n=tostring(unit.onboard_num)
local name=unit.name
local skill=unit.skill
local skill=unit.skill or "Unknown"
-- Debug text.
text=text..string.format("\n- unit %s: onboard #=%s skill=%s", name, n, skill)
text=text..string.format("\n- unit %s: onboard #=%s skill=%s", name, n, tostring(skill))
if playeronly and skill=="Client" or skill=="Player" then
-- There can be only one player in the group, so we skip everything else.
@@ -17609,7 +17645,8 @@ function AIRBOSS:_SaveTrapSheet(playerData, grade)
if self.trapprefix then
filename=string.format("%s_%s-%04d.csv", self.trapprefix, playerData.actype, i)
else
filename=string.format("AIRBOSS-%s_Trapsheet-%s_%s-%04d.csv", self.alias, playerData.name, playerData.actype, i)
local name=UTILS.ReplaceIllegalCharacters(playerData.name, "_")
filename=string.format("AIRBOSS-%s_Trapsheet-%s_%s-%04d.csv", self.alias, name, playerData.actype, i)
end
-- Set path.

View File

@@ -1345,7 +1345,6 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
group:InitModex(self.modex)
-- Respawn tanker. Delaying respawn due to DCS bug https://github.com/FlightControl-Master/MOOSE/issues/1076
--SCHEDULER:New(nil , group.RespawnAtCurrentAirbase, {group}, 1)
self:ScheduleOnce(1, GROUP.RespawnAtCurrentAirbase, group)
-- Create tanker beacon and activate TACAN.
@@ -1364,7 +1363,6 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
end
-- Initial route.
--SCHEDULER:New(nil, self._InitRoute, {self, -self.distStern+UTILS.NMToMeters(3)}, 2)
self:ScheduleOnce(2, RECOVERYTANKER._InitRoute, self, -self.distStern+UTILS.NMToMeters(3))
end

View File

@@ -876,8 +876,8 @@ function RESCUEHELO:onafterStart(From, Event, To)
local Spawn=SPAWN:NewWithAlias(self.helogroupname, self.alias)
-- Set modex for spawn.
Spawn:InitModex(self.modex)
Spawn:InitModex(self.modex)
-- Spawn in air or at airbase.
if self.takeoff==SPAWN.Takeoff.Air then

View File

@@ -56,6 +56,17 @@ ENUMS.ROT = {
AllowAbortMission=4,
}
--- Alarm state.
-- @type ENUMS.AlarmState
-- @field #number Auto AI will automatically switch alarm states based on the presence of threats. The AI kind of cheats in this regard.
-- @field #number Green Group is not combat ready. Sensors are stowed if possible.
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
ENUMS.AlarmState = {
Auto=0,
Green=1,
Red=2,
}
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
-- @type ENUMS.WeaponFlag
ENUMS.WeaponFlag={
@@ -222,6 +233,15 @@ ENUMS.Formation.RotaryWing.EchelonLeft={}
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
ENUMS.Formation.Vehicle={}
ENUMS.Formation.Vehicle.Vee="Vee"
ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
ENUMS.Formation.Vehicle.OffRoad="Off Road"
ENUMS.Formation.Vehicle.Rank="Rank"
ENUMS.Formation.Vehicle.EchelonLeft="EchelonL"
ENUMS.Formation.Vehicle.OnRoad="On Road"
ENUMS.Formation.Vehicle.Cone="Cone"
ENUMS.Formation.Vehicle.Diamond="Diamond"
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.

View File

@@ -0,0 +1,539 @@
--- **Utils** - Lua Profiler.
--
-- Find out how many times functions are called and how much real time it costs.
--
-- ===
--
-- ### Author: **TAW CougarNL**, *funkyfranky*
--
-- @module Utilities.PROFILER
-- @image MOOSE.JPG
--- PROFILER class.
-- @type PROFILER
-- @field #string ClassName Name of the class.
-- @field #table Counters Function counters.
-- @field #table dInfo Info.
-- @field #table fTime Function time.
-- @field #table fTimeTotal Total function time.
-- @field #table eventhandler Event handler to get mission end event.
-- @field #number TstartGame Game start time timer.getTime().
-- @field #number TstartOS OS real start time os.clock.
-- @field #boolean logUnknown Log unknown functions. Default is off.
-- @field #number ThreshCPS Low calls per second threshold. Only write output if function has more calls per second than this value.
-- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value.
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
--
-- ===
--
-- ![Banner Image](..\Presentations\Utilities\PROFILER_Main.jpg)
--
-- # The PROFILER Concept
--
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
-- With this information you can optimize the perfomance of your code.
--
-- # Prerequisites
--
-- The modules **os** and **lfs** need to be desanizied.
--
--
-- # Start
--
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
--
-- PROFILER.Start()
--
-- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to
-- stop the profiler after a certain amount of seconds.
--
-- # Stop
--
-- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function
--
-- PROFILER.Stop()
--
-- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped.
--
-- When the profiler is stopped, the output is written to a file.
--
-- # Output
--
-- The profiler output is written to a file in your DCS home folder
--
-- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs
--
-- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc.
--
-- ## Data
--
-- The data in the output file provides information on the functions that were called in the mission.
--
-- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time.
--
-- If you only want output for functions that are called more than *X* times per second, you can set
--
-- PROFILER.ThreshCPS=1.5
--
-- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold).
--
-- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by
--
-- PROFILER.ThreshTtot=0.005
--
-- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time.
--
-- @field #PROFILER
PROFILER = {
ClassName = "PROFILER",
Counters = {},
dInfo = {},
fTime = {},
fTimeTotal = {},
eventHandler = {},
logUnknown = false,
ThreshCPS = 0.0,
ThreshTtot = 0.005,
fileNamePrefix = "MooseProfiler",
fileNameSuffix = "txt"
}
--- Waypoint data.
-- @type PROFILER.Data
-- @field #string func The function name.
-- @field #string src The source file.
-- @field #number line The line number
-- @field #number count Number of function calls.
-- @field #number tm Total time in seconds.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start/Stop Profiler
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Start profiler.
-- @param #number Delay Delay in seconds before profiler is stated. Default is immediately.
-- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends.
function PROFILER.Start(Delay, Duration)
-- Check if os, io and lfs are available.
local go=true
if not os then
env.error("ERROR: Profiler needs os to be desanitized!")
go=false
end
if not io then
env.error("ERROR: Profiler needs io to be desanitized!")
go=false
end
if not lfs then
env.error("ERROR: Profiler needs lfs to be desanitized!")
go=false
end
if not go then
return
end
if Delay and Delay>0 then
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration)
else
-- Set start time.
PROFILER.TstartGame=timer.getTime()
PROFILER.TstartOS=os.clock()
-- Add event handler.
world.addEventHandler(PROFILER.eventHandler)
-- Info in log.
env.info('############################ Profiler Started ############################')
if Duration then
env.info(string.format("- Will be running for %d seconds", Duration))
else
env.info(string.format("- Will be stopped when mission ends"))
end
env.info(string.format("- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS))
env.info(string.format("- Total function time threshold %.3f sec", PROFILER.ThreshTtot))
env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix)))
env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv")))
env.info('###############################################################################')
-- Message on screen
local duration=Duration or 600
trigger.action.outText("### Profiler running ###", duration)
-- Set hook.
debug.sethook(PROFILER.hook, "cr")
-- Auto stop profiler.
if Duration then
PROFILER.Stop(Duration)
end
end
end
--- Stop profiler.
-- @param #number Delay Delay before stop in seconds.
function PROFILER.Stop(Delay)
if Delay and Delay>0 then
BASE:ScheduleOnce(Delay, PROFILER.Stop)
else
-- Remove hook.
debug.sethook()
-- Run time game.
local runTimeGame=timer.getTime()-PROFILER.TstartGame
-- Run time real OS.
local runTimeOS=os.clock()-PROFILER.TstartOS
-- Show info.
PROFILER.showInfo(runTimeGame, runTimeOS)
end
end
--- Event handler.
function PROFILER.eventHandler:onEvent(event)
if event.id==world.event.S_EVENT_MISSION_END then
PROFILER.Stop()
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Hook
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Debug hook.
-- @param #table event Event.
function PROFILER.hook(event)
local f=debug.getinfo(2, "f").func
if event=='call' then
if PROFILER.Counters[f]==nil then
PROFILER.Counters[f]=1
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
if PROFILER.fTimeTotal[f]==nil then
PROFILER.fTimeTotal[f]=0
end
else
PROFILER.Counters[f]=PROFILER.Counters[f]+1
end
if PROFILER.fTime[f]==nil then
PROFILER.fTime[f]=os.clock()
end
elseif (event=='return') then
if PROFILER.fTime[f]~=nil then
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
PROFILER.fTime[f]=nil
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Data
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get data.
-- @param #function func Function.
-- @return #string Function name.
-- @return #string Source file name.
-- @return #string Line number.
-- @return #number Function time in seconds.
function PROFILER.getData(func)
local n=PROFILER.dInfo[func]
if n.what=="C" then
return n.name, "?", "?", PROFILER.fTimeTotal[func]
end
return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func]
end
--- Write text to log file.
-- @param #function f The file.
-- @param #string txt The text.
function PROFILER._flog(f, txt)
f:write(txt.."\r\n")
end
--- Show table.
-- @param #table data Data table.
-- @param #function f The file.
-- @param #number runTimeGame Game run time in seconds.
function PROFILER.showTable(data, f, runTimeGame)
-- Loop over data.
for i=1, #data do
local t=data[i] --#PROFILER.Data
-- Calls per second.
local cps=t.count/runTimeGame
local threshCPS=cps>=PROFILER.ThreshCPS
local threshTot=t.tm>=PROFILER.ThreshTtot
if threshCPS and threshTot then
-- Output
local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
PROFILER._flog(f, text)
end
end
end
--- Print csv file.
-- @param #table data Data table.
-- @param #number runTimeGame Game run time in seconds.
function PROFILER.printCSV(data, runTimeGame)
-- Output file.
local file=PROFILER.getfilename("csv")
local g=io.open(file, 'w')
-- Header.
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
g:write(text.."\r\n")
-- Loop over data.
for i=1, #data do
local t=data[i] --#PROFILER.Data
-- Calls per second.
local cps=t.count/runTimeGame
-- Output
local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
g:write(txt.."\r\n")
end
-- Close file.
g:close()
end
--- Write info to output file.
-- @param #string ext Extension.
-- @return #string File name.
function PROFILER.getfilename(ext)
local dir=lfs.writedir()..[[Logs\]]
ext=ext or PROFILER.fileNameSuffix
local file=dir..PROFILER.fileNamePrefix.."."..ext
if not UTILS.FileExists(file) then
return file
end
for i=1,999 do
local file=string.format("%s%s-%03d.%s", dir,PROFILER.fileNamePrefix, i, ext)
if not UTILS.FileExists(file) then
return file
end
end
end
--- Write info to output file.
-- @param #number runTimeGame Game time in seconds.
-- @param #number runTimeOS OS time in seconds.
function PROFILER.showInfo(runTimeGame, runTimeOS)
-- Output file.
local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
local f=io.open(file, 'w')
-- Gather data.
local Ttot=0
local Calls=0
local t={}
local tcopy=nil --#PROFILER.Data
local tserialize=nil --#PROFILER.Data
local tforgen=nil --#PROFILER.Data
local tpairs=nil --#PROFILER.Data
for func, count in pairs(PROFILER.Counters) do
local s,src,line,tm=PROFILER.getData(func)
if PROFILER.logUnknown==true then
if s==nil then s="<Unknown>" end
end
if s~=nil then
-- Profile data.
local T=
{ func=s,
src=src,
line=line,
count=count,
tm=tm,
} --#PROFILER.Data
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
if s=="_copy" then
if tcopy==nil then
tcopy=T
else
tcopy.count=tcopy.count+T.count
tcopy.tm=tcopy.tm+T.tm
end
elseif s=="_Serialize" then
if tserialize==nil then
tserialize=T
else
tserialize.count=tserialize.count+T.count
tserialize.tm=tserialize.tm+T.tm
end
elseif s=="(for generator)" then
if tforgen==nil then
tforgen=T
else
tforgen.count=tforgen.count+T.count
tforgen.tm=tforgen.tm+T.tm
end
elseif s=="pairs" then
if tpairs==nil then
tpairs=T
else
tpairs.count=tpairs.count+T.count
tpairs.tm=tpairs.tm+T.tm
end
else
table.insert(t, T)
end
-- Total function time.
Ttot=Ttot+tm
-- Total number of calls.
Calls=Calls+count
end
end
-- Add special cases.
if tcopy then
table.insert(t, tcopy)
end
if tserialize then
table.insert(t, tserialize)
end
if tforgen then
table.insert(t, tforgen)
end
if tpairs then
table.insert(t, tpairs)
end
env.info('############################ Profiler Stopped ############################')
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
env.info(string.format("* Total functions : %d", #t))
env.info(string.format("* Total func calls : %d", Calls))
env.info(string.format("* Writing to file : \"%s\"", file))
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv")))
env.info("##############################################################################")
-- Sort by total time.
table.sort(t, function(a,b) return a.tm>b.tm end)
-- Write data.
PROFILER._flog(f,"")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"")
PROFILER._flog(f,"-------------------------")
PROFILER._flog(f,"---- Profiler Report ----")
PROFILER._flog(f,"-------------------------")
PROFILER._flog(f,"")
PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
PROFILER._flog(f,"")
PROFILER._flog(f,string.format("* Total functions = %d", #t))
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
PROFILER._flog(f,"")
PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS))
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot))
PROFILER._flog(f,"")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"")
PROFILER.showTable(t, f, runTimeGame)
-- Sort by number of calls.
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end)
-- Detailed data.
PROFILER._flog(f,"")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"")
PROFILER._flog(f,"--------------------------------------")
PROFILER._flog(f,"---- Data Sorted by Time per Call ----")
PROFILER._flog(f,"--------------------------------------")
PROFILER._flog(f,"")
PROFILER.showTable(t, f, runTimeGame)
-- Sort by number of calls.
table.sort(t, function(a,b) return a.count>b.count end)
-- Detailed data.
PROFILER._flog(f,"")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"")
PROFILER._flog(f,"------------------------------------")
PROFILER._flog(f,"---- Data Sorted by Total Calls ----")
PROFILER._flog(f,"------------------------------------")
PROFILER._flog(f,"")
PROFILER.showTable(t, f, runTimeGame)
-- Closing.
PROFILER._flog(f,"")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"************************************************************************************************************************")
PROFILER._flog(f,"************************************************************************************************************************")
-- Close file.
f:close()
-- Print csv file.
PROFILER.printCSV(t, runTimeGame)
end

View File

@@ -48,11 +48,15 @@ BIGSMOKEPRESET = {
-- @field #string Normandy Normandy map.
-- @field #string NTTR Nevada Test and Training Range map.
-- @field #string PersianGulf Persian Gulf map.
-- @field #string TheChannel The Channel map.
-- @field #string Syria Syria map.
DCSMAP = {
Caucasus="Caucasus",
NTTR="Nevada",
Normandy="Normandy",
PersianGulf="PersianGulf"
PersianGulf="PersianGulf",
TheChannel="TheChannel",
Syria="Syria",
}
@@ -187,21 +191,30 @@ end
-- @param #table object The input table.
-- @return #table Copy of the input table.
UTILS.DeepCopy = function(object)
local lookup_table = {}
-- Copy function.
local function _copy(object)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
local objectreturn = _copy(object)
return objectreturn
end
@@ -311,6 +324,10 @@ UTILS.MetersToNM = function(meters)
return meters/1852
end
UTILS.MetersToSM = function(meters)
return meters/1609.34
end
UTILS.MetersToFeet = function(meters)
return meters/0.3048
end
@@ -351,7 +368,7 @@ UTILS.MpsToMiph = function( mps )
end
--- Convert meters per second to knots.
-- @param #number knots Speed in m/s.
-- @param #number mps Speed in m/s.
-- @return #number Speed in knots.
UTILS.MpsToKnots = function( mps )
return mps * 1.94384 --3600 / 1852
@@ -718,7 +735,8 @@ function UTILS.SecondsToClock(seconds, short)
local clock=hours..":"..mins..":"..secs.."+"..days
if short then
if hours=="00" then
clock=mins..":"..secs
--clock=mins..":"..secs
clock=hours..":"..mins..":"..secs
else
clock=hours..":"..mins..":"..secs
end
@@ -727,6 +745,26 @@ function UTILS.SecondsToClock(seconds, short)
end
end
--- Seconds of today.
-- @return #number Seconds passed since last midnight.
function UTILS.SecondsOfToday()
-- Time in seconds.
local time=timer.getAbsTime()
-- Short format without days since mission start.
local clock=UTILS.SecondsToClock(time, true)
-- Time is now the seconds passed since last midnight.
return UTILS.ClockToSeconds(clock)
end
--- Cound seconds until next midnight.
-- @return #number Seconds to midnight.
function UTILS.SecondsToMidnight()
return 24*60*60-UTILS.SecondsOfToday()
end
--- Convert clock time from hours, minutes and seconds to seconds.
-- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days.
-- @return #number Seconds. Corresponds to what you cet from timer.getAbsTime() function.
@@ -783,6 +821,15 @@ function UTILS.DisplayMissionTime(duration)
MESSAGE:New(text, duration):ToAll()
end
--- Replace illegal characters [<>|/?*:\\] in a string.
-- @param #string Text Input text.
-- @param #string ReplaceBy Replace illegal characters by this character or string. Default underscore "_".
-- @return #string The input text with illegal chars replaced.
function UTILS.ReplaceIllegalCharacters(Text, ReplaceBy)
ReplaceBy=ReplaceBy or "_"
local text=Text:gsub("[<>|/?*:\\]", ReplaceBy)
return text
end
--- Generate a Gaussian pseudo-random number.
-- @param #number x0 Expectation value of distribution.
@@ -937,6 +984,22 @@ function UTILS.HdgDiff(h1, h2)
end
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number distance The distance to translate.
-- @param #number angle Rotation angle in degrees.
-- @return DCS#Vec3 Vector rotated in the (x,z) plane.
function UTILS.VecTranslate(a, distance, angle)
local SX = a.x
local SY = a.z
local Radians=math.rad(angle or 0)
local TX=distance*math.cos(Radians)+SX
local TY=distance*math.sin(Radians)+SY
return {x=TX, y=a.y, z=TY}
end
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number angle Rotation angle in degrees.
@@ -958,7 +1021,6 @@ function UTILS.Rotate2D(a, angle)
end
--- Converts a TACAN Channel/Mode couple into a frequency in Hz.
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
-- @param #string TACANMode The TACAN mode, i.e. the "X" in "10X".
@@ -1002,15 +1064,64 @@ function UTILS.GetDCSMap()
return env.mission.theatre
end
--- Returns the mission date. This is the date the mission started.
--- Returns the mission date. This is the date the mission **started**.
-- @return #string Mission date in yyyy/mm/dd format.
-- @return #number The year anno domini.
-- @return #number The month.
-- @return #number The day.
function UTILS.GetDCSMissionDate()
local year=tostring(env.mission.date.Year)
local month=tostring(env.mission.date.Month)
local day=tostring(env.mission.date.Day)
return string.format("%s/%s/%s", year, month, day)
return string.format("%s/%s/%s", year, month, day), tonumber(year), tonumber(month), tonumber(day)
end
--- Returns the day of the mission.
-- @param #number Time (Optional) Abs. time in seconds. Default now, i.e. the value return from timer.getAbsTime().
-- @return #number Day of the mission. Mission starts on day 0.
function UTILS.GetMissionDay(Time)
Time=Time or timer.getAbsTime()
local clock=UTILS.SecondsToClock(Time, false)
local x=tonumber(UTILS.Split(clock, "+")[2])
return x
end
--- Returns the current day of the year of the mission.
-- @param #number Time (Optional) Abs. time in seconds. Default now, i.e. the value return from timer.getAbsTime().
-- @return #number Current day of year of the mission. For example, January 1st returns 0, January 2nd returns 1 etc.
function UTILS.GetMissionDayOfYear(Time)
local Date, Year, Month, Day=UTILS.GetDCSMissionDate()
local d=UTILS.GetMissionDay(Time)
return UTILS.GetDayOfYear(Year, Month, Day)+d
end
--- Returns the current date.
-- @return #string Mission date in yyyy/mm/dd format.
-- @return #number The year anno domini.
-- @return #number The month.
-- @return #number The day.
function UTILS.GetDate()
-- Mission start date
local date, year, month, day=UTILS.GetDCSMissionDate()
local time=timer.getAbsTime()
local clock=UTILS.SecondsToClock(time, false)
local x=tonumber(UTILS.Split(clock, "+")[2])
local day=day+x
end
--- Returns the magnetic declination of the map.
-- Returned values for the current maps are:
@@ -1035,6 +1146,10 @@ function UTILS.GetMagneticDeclination(map)
declination=-10
elseif map==DCSMAP.PersianGulf then
declination=2
elseif map==DCSMAP.TheChannel then
declination=-10
elseif map==DCSMAP.Syria then
declination=5
else
declination=0
end
@@ -1144,3 +1259,189 @@ function UTILS.GetCallsignName(Callsign)
return "Ghostrider"
end
--- Get the time difference between GMT and local time.
-- @return #number Local time difference in hours compared to GMT. E.g. Dubai is GMT+4 ==> +4 is returned.
function UTILS.GMTToLocalTimeDifference()
local theatre=UTILS.GetDCSMap()
if theatre==DCSMAP.Caucasus then
return 4 -- Caucasus UTC+4 hours
elseif theatre==DCSMAP.PersianGulf then
return 4 -- Abu Dhabi UTC+4 hours
elseif theatre==DCSMAP.NTTR then
return -8 -- Las Vegas UTC-8 hours
elseif theatre==DCSMAP.Normandy then
return 0 -- Calais UTC+1 hour
elseif theatre==DCSMAP.TheChannel then
return 2 -- This map currently needs +2
elseif theatre==DCSMAP.Syria then
return 3 -- Damascus is UTC+3 hours
else
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
return 0
end
end
--- Get the day of the year. Counting starts on 1st of January.
-- @param #number Year The year.
-- @param #number Month The month.
-- @param #number Day The day.
-- @return #number The day of the year.
function UTILS.GetDayOfYear(Year, Month, Day)
local floor = math.floor
local n1 = floor(275 * Month / 9)
local n2 = floor((Month + 9) / 12)
local n3 = (1 + floor((Year - 4 * floor(Year / 4) + 2) / 3))
return n1 - (n2 * n3) + Day - 30
end
--- Get sunrise or sun set of a specific day of the year at a specific location.
-- @param #number DayOfYear The day of the year.
-- @param #number Latitude Latitude.
-- @param #number Longitude Longitude.
-- @param #boolean Rising If true, calc sun rise, or sun set otherwise.
-- @param #number Tlocal Local time offset in hours. E.g. +4 for a location which has GMT+4.
-- @return #number Sun rise/set in seconds of the day.
function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
-- Defaults
local zenith=90.83
local latitude=Latitude
local longitude=Longitude
local rising=Rising
local n=DayOfYear
Tlocal=Tlocal or 0
-- Short cuts.
local rad = math.rad
local deg = math.deg
local floor = math.floor
local frac = function(n) return n - floor(n) end
local cos = function(d) return math.cos(rad(d)) end
local acos = function(d) return deg(math.acos(d)) end
local sin = function(d) return math.sin(rad(d)) end
local asin = function(d) return deg(math.asin(d)) end
local tan = function(d) return math.tan(rad(d)) end
local atan = function(d) return deg(math.atan(d)) end
local function fit_into_range(val, min, max)
local range = max - min
local count
if val < min then
count = floor((min - val) / range) + 1
return val + count * range
elseif val >= max then
count = floor((val - max) / range) + 1
return val - count * range
else
return val
end
end
-- Convert the longitude to hour value and calculate an approximate time
local lng_hour = longitude / 15
local t
if rising then -- Rising time is desired
t = n + ((6 - lng_hour) / 24)
else -- Setting time is desired
t = n + ((18 - lng_hour) / 24)
end
-- Calculate the Sun's mean anomaly
local M = (0.9856 * t) - 3.289
-- Calculate the Sun's true longitude
local L = fit_into_range(M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634, 0, 360)
-- Calculate the Sun's right ascension
local RA = fit_into_range(atan(0.91764 * tan(L)), 0, 360)
-- Right ascension value needs to be in the same quadrant as L
local Lquadrant = floor(L / 90) * 90
local RAquadrant = floor(RA / 90) * 90
RA = RA + Lquadrant - RAquadrant
-- Right ascension value needs to be converted into hours
RA = RA / 15
-- Calculate the Sun's declination
local sinDec = 0.39782 * sin(L)
local cosDec = cos(asin(sinDec))
-- Calculate the Sun's local hour angle
local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
if rising and cosH > 1 then
return "N/R" -- The sun never rises on this location on the specified date
elseif cosH < -1 then
return "N/S" -- The sun never sets on this location on the specified date
end
-- Finish calculating H and convert into hours
local H
if rising then
H = 360 - acos(cosH)
else
H = acos(cosH)
end
H = H / 15
-- Calculate local mean time of rising/setting
local T = H + RA - (0.06571 * t) - 6.622
-- Adjust back to UTC
local UT = fit_into_range(T - lng_hour +Tlocal, 0, 24)
return floor(UT)*60*60+frac(UT)*60*60--+Tlocal*60*60
end
--- Get sun rise of a specific day of the year at a specific location.
-- @param #number Day Day of the year.
-- @param #number Month Month of the year.
-- @param #number Year Year.
-- @param #number Latitude Latitude.
-- @param #number Longitude Longitude.
-- @param #boolean Rising If true, calc sun rise, or sun set otherwise.
-- @param #number Tlocal Local time offset in hours. E.g. +4 for a location which has GMT+4. Default 0.
-- @return #number Sun rise in seconds of the day.
function UTILS.GetSunrise(Day, Month, Year, Latitude, Longitude, Tlocal)
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
return UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tlocal)
end
--- Get sun set of a specific day of the year at a specific location.
-- @param #number Day Day of the year.
-- @param #number Month Month of the year.
-- @param #number Year Year.
-- @param #number Latitude Latitude.
-- @param #number Longitude Longitude.
-- @param #boolean Rising If true, calc sun rise, or sun set otherwise.
-- @param #number Tlocal Local time offset in hours. E.g. +4 for a location which has GMT+4. Default 0.
-- @return #number Sun rise in seconds of the day.
function UTILS.GetSunset(Day, Month, Year, Latitude, Longitude, Tlocal)
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
return UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tlocal)
end
--- Get OS time. Needs os to be desanitized!
-- @return #number Os time in seconds.
function UTILS.GetOSTime()
if os then
return os.clock()
end
return nil
end

File diff suppressed because it is too large Load Diff

View File

@@ -243,8 +243,7 @@ end
--- Returns the initial health.
-- @param #CONTROLLABLE self
-- @return #number The controllable health value (unit or group average).
-- @return #nil The controllable is not existing or alive.
-- @return #number The controllable health value (unit or group average) or `nil` if the controllable does not exist.
function CONTROLLABLE:GetLife0()
self:F2( self.ControllableName )
@@ -296,7 +295,6 @@ end
-- @return #nil The CONTROLLABLE is not existing or alive.
function CONTROLLABLE:GetFuel()
self:F( self.ControllableName )
return nil
end
@@ -803,7 +801,7 @@ function CONTROLLABLE:CommandSetFrequency(Frequency, Modulation, Delay)
local CommandSetFrequency = {
id = 'SetFrequency',
params = {
frequency = Frequency,
frequency = Frequency*1000000,
modulation = Modulation or radio.modulation.AM,
}
}
@@ -1429,16 +1427,6 @@ end
-- @return DCS#Task The DCS task structure.
function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
-- FireAtPoint = {
-- id = 'FireAtPoint',
-- params = {
-- point = Vec2,
-- radius = Distance,
-- expendQty = number,
-- expendQtyEnabled = boolean,
-- }
-- }
local DCSTask = {
id = 'FireAtPoint',
params = {
@@ -1458,7 +1446,6 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
DCSTask.params.weaponType=WeaponType
end
self:T3( { DCSTask } )
return DCSTask
end
@@ -1481,8 +1468,8 @@ end
-- @param #number WeaponType Bitmask of weapon types, which are allowed to use.
-- @param DCS#AI.Task.Designation Designation (Optional) Designation type.
-- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
-- @param #number Frequency Frequency used to communicate with the FAC.
-- @param #number Modulation Modulation of radio for communication.
-- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz.
-- @param #number Modulation Modulation of radio for communication. Default 0=AM.
-- @param #number CallsignName Callsign enumerator name of the FAC.
-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**.
-- @return DCS#Task The DCS task structure.
@@ -1492,11 +1479,11 @@ function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation,
id = 'FAC_AttackGroup',
params = {
groupId = AttackGroup:GetID(),
weaponType = WeaponType,
designation = Designation,
datalink = Datalink,
frequency = Frequency,
modulation = Modulation,
weaponType = WeaponType or ENUMS.WeaponFlag.AutoDCS,
designation = Designation or "Auto",
datalink = Datalink and Datalink or true,
frequency = (Frequency or 133)*1000000,
modulation = Modulation or radio.modulation.AM,
callname = CallsignName,
number = CallsignNumber,
}
@@ -2841,7 +2828,7 @@ function CONTROLLABLE:GetDetectedUnitSet(DetectVisual, DetectOptical, DetectRada
return unitset
end
--- Return the detected target groups of the controllable as a @{SET_GROUP}.
--- Return the detected target groups of the controllable as a @{Core.Set#SET_GROUP}.
-- The optional parametes specify the detection methods that can be applied.
-- If no detection method is given, the detection will use all the available methods by default.
-- @param Wrapper.Controllable#CONTROLLABLE self

View File

@@ -261,7 +261,9 @@ end
-- @param #string GroupName The Group name
-- @return #GROUP self
function GROUP:Register( GroupName )
local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) -- #GROUP
self.GroupName = GroupName
self:SetEventPriority( 4 )
@@ -668,14 +670,15 @@ end
-- @param #number UnitNumber The number of the UNIT wrapper class to be returned.
-- @return Wrapper.Unit#UNIT The UNIT wrapper class.
function GROUP:GetUnit( UnitNumber )
self:F3( { self.GroupName, UnitNumber } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnit = DCSGroup:getUnit( UnitNumber )
local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) )
self:T2( UnitFound )
local UnitFound = UNIT:Find(DCSUnit)
return UnitFound
end
@@ -688,13 +691,11 @@ end
-- @param #number UnitNumber The number of the DCS Unit to be returned.
-- @return DCS#Unit The DCS Unit.
function GROUP:GetDCSUnit( UnitNumber )
self:F3( { self.GroupName, UnitNumber } )
local DCSGroup = self:GetDCSObject()
local DCSGroup=self:GetDCSObject()
if DCSGroup then
local DCSUnitFound = DCSGroup:getUnit( UnitNumber )
self:T3( DCSUnitFound )
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
return DCSUnitFound
end
@@ -706,14 +707,14 @@ end
-- @param #GROUP self
-- @return #number The DCS Group size.
function GROUP:GetSize()
self:F3( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupSize = DCSGroup:getSize()
if GroupSize then
self:T3( GroupSize )
return GroupSize
else
return 0
@@ -946,24 +947,31 @@ end
-- @param #GROUP self
-- @return DCS#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group.
function GROUP:GetVec2()
self:F2( self.GroupName )
local UnitPoint = self:GetUnit(1)
UnitPoint:GetVec2()
local GroupPointVec2 = UnitPoint:GetVec2()
self:T3( GroupPointVec2 )
return GroupPointVec2
local Unit=self:GetUnit(1)
if Unit then
local vec2=Unit:GetVec2()
return vec2
end
end
--- Returns the current Vec3 vector of the first DCS Unit in the GROUP.
-- @param #GROUP self
-- @return DCS#Vec3 Current Vec3 of the first DCS Unit of the GROUP.
function GROUP:GetVec3()
self:F2( self.GroupName )
local GroupVec3 = self:GetUnit(1):GetVec3()
self:T3( GroupVec3 )
return GroupVec3
-- Get first unit.
local unit=self:GetUnit(1)
if unit then
local vec3=unit:GetVec3()
return vec3
end
self:E("ERROR: Cannot get Vec3 of group "..tostring(self.GroupName))
return nil
end
--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission.
@@ -990,13 +998,11 @@ end
-- @param Wrapper.Group#GROUP self
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
function GROUP:GetCoordinate()
self:F2( self.PositionableName )
local FirstUnit = self:GetUnit(1)
if FirstUnit then
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
self:T3(FirstUnitCoordinate)
return FirstUnitCoordinate
end
@@ -1039,11 +1045,16 @@ function GROUP:GetHeading()
local GroupSize = self:GetSize()
local HeadingAccumulator = 0
local n=0
if GroupSize then
for i = 1, GroupSize do
HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading()
local unit=self:GetUnit(i)
if unit and unit:IsAlive() then
HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
n=n+1
end
end
return math.floor(HeadingAccumulator / GroupSize)
return math.floor(HeadingAccumulator / n)
end
BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } )
@@ -1161,6 +1172,35 @@ end
do -- Is Zone methods
--- Check if any unit of a group is inside a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns `true` if *at least one unit* is inside the zone or `false` if *no* unit is inside.
function GROUP:IsInZone( Zone )
if self:IsAlive() then
for UnitID, UnitData in pairs(self:GetUnits()) do
local Unit = UnitData -- Wrapper.Unit#UNIT
-- Get 2D vector. That's all we need for the zone check.
local vec2=Unit:GetVec2()
if Zone:IsVec2InZone(vec2) then
return true -- At least one unit is in the zone. That is enough.
else
-- This one is not but another could be.
end
end
return false
end
return nil
end
--- Returns true if all units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
@@ -2095,6 +2135,7 @@ end
--- Calculate the maxium A2G threat level of the Group.
-- @param #GROUP self
-- @return #number Number between 0 and 10.
function GROUP:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
@@ -2110,6 +2151,25 @@ function GROUP:CalculateThreatLevelA2G()
return MaxThreatLevelA2G
end
--- Get threat level of the group.
-- @param #GROUP self
-- @return #number Max threat level (a number between 0 and 10).
function GROUP:GetThreatLevel()
local threatlevelMax = 0
for UnitName, UnitData in pairs(self:GetUnits()) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local threatlevel = ThreatUnit:GetThreatLevel()
if threatlevel > threatlevelMax then
threatlevelMax=threatlevel
end
end
return threatlevelMax
end
--- Returns true if the first unit of the GROUP is in the air.
-- @param Wrapper.Group#GROUP self
-- @return #boolean true if in the first unit of the group is in the air or #nil if the GROUP is not existing or not alive.
@@ -2142,27 +2202,52 @@ function GROUP:IsAirborne(AllUnits)
if units then
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit then
-- Unit in air or not.
local inair=unit:InAir()
if AllUnits then
--- We want to know if ALL units are airborne.
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
-- Unit is not in air and we wanted to know whether ALL units are ==> return false
if inair==false and AllUnits==true then
return false
end
if unit then
-- At least one unit is in are and we did not care which one.
if inair==true and not AllUnits then
return true
-- Unit in air or not.
local inair=unit:InAir()
-- At least one unit is not in air.
if not inair then
return false
end
end
end
-- At least one unit is in the air.
return true
-- All units are in air.
return true
else
--- We want to know if ANY unit is airborne.
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit then
-- Unit in air or not.
local inair=unit:InAir()
if inair then
-- At least one unit is in air.
return true
end
end
-- No unit is in air.
return false
end
end
end

View File

@@ -0,0 +1,776 @@
--- **Wrapper** - Markers On the F10 map.
--
-- **Main Features:**
--
-- * Convenient handling of markers via multiple user API functions.
-- * Update text and position of marker easily via scripting.
-- * Delay creation and removal of markers via (optional) parameters.
-- * Retrieve data such as text and coordinate.
-- * Marker specific FSM events when a marker is added, removed or changed.
-- * Additional FSM events when marker text or position is changed.
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Wrapper.Marker
-- @image Wrapper_Marker.png
--- Marker class.
-- @type MARKER
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file.
-- @field #number mid Marker ID.
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
-- @field #string text Text displayed in the mark panel.
-- @field #string message Message dispayed when the mark is added.
-- @field #boolean readonly Marker is read-only.
-- @field #number coalition Coalition to which the marker is displayed.
-- @extends Core.Fsm#FSM
--- Just because...
--
-- ===
--
-- ![Banner Image](..\Presentations\MARKER\Marker_Main.jpg)
--
-- # The MARKER Class Idea
--
-- The MARKER class simplifies creating, updating and removing of markers on the F10 map.
--
-- # Create a Marker
--
-- -- Create a MARKER object at Batumi with a trivial text.
-- local Coordinate=AIRBASE:FindByName("Batumi"):GetCoordinate()
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield")
--
-- Now this does **not** show the marker yet. We still need to specifiy to whom it is shown. There are several options, i.e.
-- show the marker to everyone, to a speficic coaliton only, or only to a specific group.
--
-- ## For Everyone
--
-- If the marker should be visible to everyone, you can use the :ToAll() function.
--
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToAll()
--
-- ## For a Coaliton
--
-- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function.
--
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToCoaliton(coaliton.side.BLUE)
--
-- ### To Blue Coaliton
--
-- ### To Red Coalition
--
-- This would show the marker only to the Blue coaliton.
--
-- ## For a Group
--
--
-- # Removing a Marker
--
--
-- # Updating a Marker
--
-- The marker text and coordinate can be updated easily as shown below.
--
-- However, note that **updateing involves to remove and recreate the marker if either text or its coordinate is changed**.
-- *This is a DCS scripting engine limitation.*
--
-- ## Update Text
--
-- If you created a marker "mymarker" as shown above, you can update the dispayed test by
--
-- mymarker:UpdateText("I am the new text at Batumi")
--
-- The update can also be delayed by, e.g. 90 seconds, using
--
-- mymarker:UpdateText("I am the new text at Batumi", 90)
--
-- ## Update Coordinate
--
-- If you created a marker "mymarker" as shown above, you can update its coordinate on the F10 map by
--
-- mymarker:UpdateCoordinate(NewCoordinate)
--
-- The update can also be delayed by, e.g. 60 seconds, using
--
-- mymarker:UpdateCoordinate(NewCoordinate, 60)
--
-- # Retrieve Data
--
-- The important data as the displayed text and the coordinate of the marker can be retrieved easily.
--
-- ## Text
--
-- local text=mymarker:GetText()
-- env.info("Marker Text = " .. text)
--
-- ## Coordinate
--
-- local Coordinate=mymarker:GetCoordinate()
-- env.info("Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS())
--
--
-- # FSM Events
--
-- Moose creates addditonal events, so called FSM event, when markers are added, changed, removed, and text or the coordianteis updated.
--
-- These events can be captured and used for processing via OnAfter functions as shown below.
--
-- ## Added
--
-- ## Changed
--
-- ## Removed
--
-- ## TextUpdate
--
-- ## CoordUpdate
--
--
-- # Examples
--
--
-- @field #MARKER
MARKER = {
ClassName = "MARKER",
Debug = false,
lid = nil,
mid = nil,
coordinate = nil,
text = nil,
message = nil,
readonly = nil,
coalition = nil,
}
--- Marker ID. Running number.
_MARKERID=0
--- Marker class version.
-- @field #string version
MARKER.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: User "Get" functions. E.g., :GetCoordinate()
-- DONE: Add delay to user functions.
-- DONE: Handle events.
-- DONE: Create FSM events.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new MARKER class object.
-- @param #MARKER self
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
-- @param #string Text Text displayed on the mark panel.
-- @return #MARKER self
function MARKER:New(Coordinate, Text)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
self.coordinate=Coordinate
self.text=Text
-- Defaults
self.readonly=false
self.message=""
-- New marker ID. This is not the one of the actual marker.
_MARKERID=_MARKERID+1
self.myid=_MARKERID
-- Log ID.
self.lid=string.format("Marker #%d | ", self.myid)
-- Start State.
self:SetStartState("Invisible")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Invisible", "Added", "Visible") -- Marker was added.
self:AddTransition("Visible", "Removed", "Invisible") -- Marker was removed.
self:AddTransition("*", "Changed", "*") -- Marker was changed.
self:AddTransition("*", "TextUpdate", "*") -- Text updated.
self:AddTransition("*", "CoordUpdate", "*") -- Coordinates updated.
--- Triggers the FSM event "Added".
-- @function [parent=#MARKER] Added
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the delayed FSM event "Added".
-- @function [parent=#MARKER] __Added
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData Event data table.
--- On after "Added" event user function.
-- @function [parent=#MARKER] OnAfterAdded
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "Removed".
-- @function [parent=#MARKER] Removed
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the delayed FSM event "Removed".
-- @function [parent=#MARKER] __Removed
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData Event data table.
--- On after "Removed" event user function.
-- @function [parent=#MARKER] OnAfterRemoved
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "Changed".
-- @function [parent=#MARKER] Changed
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the delayed FSM event "Changed".
-- @function [parent=#MARKER] __Changed
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData Event data table.
--- On after "Changed" event user function.
-- @function [parent=#MARKER] OnAfterChanged
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "TextUpdate".
-- @function [parent=#MARKER] TextUpdate
-- @param #MARKER self
-- @param #string Text The new text.
--- Triggers the delayed FSM event "TextUpdate".
-- @function [parent=#MARKER] __TextUpdate
-- @param #MARKER self
-- @param #string Text The new text.
--- On after "TextUpdate" event user function.
-- @function [parent=#MARKER] OnAfterTextUpdate
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string Text The new text.
--- Triggers the FSM event "CoordUpdate".
-- @function [parent=#MARKER] CoordUpdate
-- @param #MARKER self
-- @param Core.Point#COORDINATE Coordinate The new Coordinate.
--- Triggers the delayed FSM event "CoordUpdate".
-- @function [parent=#MARKER] __CoordUpdate
-- @param #MARKER self
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
--- On after "CoordUpdate" event user function.
-- @function [parent=#MARKER] OnAfterCoordUpdate
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
-- Handle events.
self:HandleEvent(EVENTS.MarkAdded)
self:HandleEvent(EVENTS.MarkRemoved)
self:HandleEvent(EVENTS.MarkChange)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User API Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Marker is readonly. Text cannot be changed and marker cannot be removed.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:ReadOnly()
self.readonly=true
return self
end
--- Set message that is displayed on screen if the marker is added.
-- @param #MARKER self
-- @param #string Text Message displayed when the marker is added.
-- @return #MARKER self
function MARKER:Message(Text)
self.message=Text or ""
return self
end
--- Place marker visible for everyone.
-- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:ToAll(Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MARKER.ToAll, self)
else
self.toall=true
self.tocoaliton=nil
self.coalition=nil
self.togroup=nil
self.groupname=nil
self.groupid=nil
-- First remove an existing mark.
if self.shown then
self:Remove()
end
self.mid=UTILS.GetMarkID()
-- Call DCS function.
trigger.action.markToAll(self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message)
end
return self
end
--- Place marker visible for a specific coalition only.
-- @param #MARKER self
-- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coaliton.side.RED`.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:ToCoalition(Coalition, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MARKER.ToCoalition, self, Coalition)
else
self.coalition=Coalition
self.tocoaliton=true
self.toall=false
self.togroup=false
self.groupname=nil
self.groupid=nil
-- First remove an existing mark.
if self.shown then
self:Remove()
end
self.mid=UTILS.GetMarkID()
-- Call DCS function.
trigger.action.markToCoalition(self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message)
end
return self
end
--- Place marker visible for the blue coalition only.
-- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:ToBlue(Delay)
self:ToCoalition(coalition.side.BLUE, Delay)
return self
end
--- Place marker visible for the blue coalition only.
-- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:ToRed(Delay)
self:ToCoalition(coalition.side.RED, Delay)
return self
end
--- Place marker visible for the neutral coalition only.
-- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:ToNeutral(Delay)
self:ToCoalition(coalition.side.NEUTRAL, Delay)
return self
end
--- Place marker visible for a specific group only.
-- @param #MARKER self
-- @param Wrapper.Group#GROUP Group The group to which the marker is displayed.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:ToGroup(Group, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MARKER.ToGroup, self, Group)
else
-- Check if group exists.
if Group and Group:IsAlive()~=nil then
self.groupid=Group:GetID()
if self.groupid then
self.groupname=Group:GetName()
self.togroup=true
self.tocoaliton=nil
self.coalition=nil
self.toall=nil
-- First remove an existing mark.
if self.shown then
self:Remove()
end
self.mid=UTILS.GetMarkID()
-- Call DCS function.
trigger.action.markToGroup(self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message)
end
else
--TODO: Warning!
end
end
return self
end
--- Update the text displayed on the mark panel.
-- @param #MARKER self
-- @param #string Text Updated text.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:UpdateText(Text, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MARKER.UpdateText, self, Text)
else
self.text=tostring(Text)
self:Refresh()
self:TextUpdate(tostring(Text))
end
return self
end
--- Update the coordinate where the marker is displayed.
-- @param #MARKER self
-- @param Core.Point#COORDINATE Coordinate The new coordinate.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:UpdateCoordinate(Coordinate, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MARKER.UpdateCoordinate, self, Coordinate)
else
self.coordinate=Coordinate
self:Refresh()
self:CoordUpdate(Coordinate)
end
return self
end
--- Refresh the marker.
-- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self
function MARKER:Refresh(Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MARKER.Refresh, self)
else
if self.toall then
self:ToAll()
elseif self.tocoaliton then
self:ToCoalition(self.coalition)
elseif self.togroup then
local group=GROUP:FindByName(self.groupname)
self:ToGroup(group)
else
self:E(self.lid.."ERROR: unknown To in :Refresh()!")
end
end
return self
end
--- Remove a marker.
-- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is removed.
-- @return #MARKER self
function MARKER:Remove(Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MARKER.Remove, self)
else
if self.shown then
-- Call DCS function.
trigger.action.removeMark(self.mid)
end
end
return self
end
--- Get position of the marker.
-- @param #MARKER self
-- @return Core.Point#COORDINATE The coordinate of the marker.
function MARKER:GetCoordinate()
return self.coordinate
end
--- Get text that is displayed in the marker panel.
-- @param #MARKER self
-- @return #string Marker text.
function MARKER:GetText()
return self.text
end
--- Set text that is displayed in the marker panel. Note this does not show the marker.
-- @param #MARKER self
-- @param #string Text Marker text. Default is an empty sting "".
-- @return #MARKER self
function MARKER:SetText(Text)
self.text=Text and tostring(Text) or ""
return self
end
--- Check if marker is currently visible on the F10 map.
-- @param #MARKER self
-- @return #boolean True if the marker is currently visible.
function MARKER:IsVisible()
return self:Is("Visible")
end
--- Check if marker is currently invisible on the F10 map.
-- @param #MARKER self
-- @return
function MARKER:IsInvisible()
return self:Is("Invisible")
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Event Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event function when a MARKER is added.
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkAdded(EventData)
if EventData and EventData.MarkID then
local MarkID=EventData.MarkID
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
if MarkID==self.mid then
self.shown=true
self:Added(EventData)
end
end
end
--- Event function when a MARKER is removed.
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkRemoved(EventData)
if EventData and EventData.MarkID then
local MarkID=EventData.MarkID
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
if MarkID==self.mid then
self.shown=false
self:Removed(EventData)
end
end
end
--- Event function when a MARKER changed.
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkChange(EventData)
if EventData and EventData.MarkID then
local MarkID=EventData.MarkID
self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s", tostring(MarkID)))
if MarkID==self.mid then
self:Changed(EventData)
self:TextChanged(tostring(EventData.MarkText))
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Event Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "Added" event.
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table.
function MARKER:onafterAdded(From, Event, To, EventData)
-- Debug info.
local text=string.format("Captured event MarkAdded for myself:\n")
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
self:T2(self.lid..text)
end
--- On after "Removed" event.
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table.
function MARKER:onafterRemoved(From, Event, To, EventData)
-- Debug info.
local text=string.format("Captured event MarkRemoved for myself:\n")
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
self:T2(self.lid..text)
end
--- On after "Changed" event.
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table.
function MARKER:onafterChanged(From, Event, To, EventData)
-- Debug info.
local text=string.format("Captured event MarkChange for myself:\n")
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
self:T2(self.lid..text)
end
--- On after "TextUpdate" event.
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string Text The updated text, displayed in the mark panel.
function MARKER:onafterTextUpdate(From, Event, To, Text)
self:T(self.lid..string.format("New Marker Text:\n%s", Text))
end
--- On after "CoordUpdate" event.
-- @param #MARKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The updated coordinates.
function MARKER:onafterCoordUpdate(From, Event, To, Coordinate)
self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS()))
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@@ -194,7 +194,7 @@ end
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
-- @param #number Delay Delay in seconds before the static is spawned.
function STATIC:SpawnAt( Coordinate, Heading, Delay )
function STATIC:SpawnAt(Coordinate, Heading, Delay)
Heading=Heading or 0
@@ -202,51 +202,58 @@ function STATIC:SpawnAt( Coordinate, Heading, Delay )
SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
end
return self
end
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
-- This is useful to respawn a cargo after it has been destroyed.
-- @param #STATIC self
-- @param DCS#country.id countryid The country ID used for spawning the new static. Default is same as currently.
-- @param #number Delay Delay in seconds before static is respawned.
function STATIC:ReSpawn(countryid, Delay)
countryid=countryid or self:GetCountry()
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawn(CountryID, Delay)
if Delay and Delay>0 then
SCHEDULER:New(nil, self.ReSpawn, {self, countryid}, Delay)
SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, countryid )
CountryID=CountryID or self:GetCountry()
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
SpawnStatic:ReSpawn()
SpawnStatic:Spawn(nil, self.StaticName)
end
return self
end
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
-- @param #STATIC self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
-- @param #number Delay Delay in seconds before static is respawned.
function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading.
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
Heading=Heading or 0
--Heading=Heading or 0
if Delay and Delay>0 then
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
SpawnStatic:ReSpawnAt( Coordinate, Heading )
end
return self
end

View File

@@ -258,8 +258,7 @@ end
--- Returns if the unit is activated.
-- @param #UNIT self
-- @return #boolean true if Unit is activated.
-- @return #nil The DCS Unit is not existing or alive.
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
function UNIT:IsActive()
self:F2( self.UnitName )
@@ -279,9 +278,7 @@ end
-- If the Unit is alive and active, true is returned.
-- If the Unit is alive but not active, false is returned.
-- @param #UNIT self
-- @return #boolean true if Unit is alive and active.
-- @return #boolean false if Unit is alive but not active.
-- @return #nil if the Unit is not existing or is not alive.
-- @return #boolean `true` if Unit is alive and active. `false` if Unit is alive but not active. `nil` if the Unit is not existing or is not alive.
function UNIT:IsAlive()
self:F3( self.UnitName )
@@ -300,7 +297,6 @@ end
--- Returns the Unit's callsign - the localized string.
-- @param #UNIT self
-- @return #string The Callsign of the Unit.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetCallsign()
self:F2( self.UnitName )
@@ -640,8 +636,7 @@ end
--- Returns the unit sensors.
-- @param #UNIT self
-- @return DCS#Unit.Sensors
-- @return #nil The DCS Unit is not existing or alive.
-- @return DCS#Unit.Sensors Table of sensors.
function UNIT:GetSensors()
self:F2( self.UnitName )
@@ -661,7 +656,6 @@ end
--- Returns if the unit has sensors of a certain type.
-- @param #UNIT self
-- @return #boolean returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:HasSensors( ... )
self:F2( arg )
@@ -678,7 +672,6 @@ end
--- Returns if the unit is SEADable.
-- @param #UNIT self
-- @return #boolean returns true if the unit is SEADable.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:HasSEAD()
self:F2()
@@ -705,7 +698,6 @@ end
-- @param #UNIT self
-- @return #boolean Indicates if at least one of the unit's radar(s) is on.
-- @return DCS#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetRadar()
self:F2( self.UnitName )
@@ -1188,23 +1180,26 @@ do -- Event Handling
--- Subscribe to a DCS Event.
-- @param #UNIT self
-- @param Core.Event#EVENTS Event
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
-- @return #UNIT
function UNIT:HandleEvent( Event, EventFunction )
-- @param Core.Event#EVENTS EventID Event ID.
-- @param #function EventFunction (Optional) The function to be called when the event occurs for the unit.
-- @return #UNIT self
function UNIT:HandleEvent(EventID, EventFunction)
self:EventDispatcher():OnEventForUnit( self:GetName(), EventFunction, self, Event )
self:EventDispatcher():OnEventForUnit(self:GetName(), EventFunction, self, EventID)
return self
end
--- UnSubscribe to a DCS event.
-- @param #UNIT self
-- @param Core.Event#EVENTS Event
-- @return #UNIT
function UNIT:UnHandleEvent( Event )
-- @param Core.Event#EVENTS EventID Event ID.
-- @return #UNIT self
function UNIT:UnHandleEvent(EventID)
self:EventDispatcher():RemoveForUnit( self:GetName(), self, Event )
--self:EventDispatcher():RemoveForUnit( self:GetName(), self, EventID )
-- Fixes issue #1365 https://github.com/FlightControl-Master/MOOSE/issues/1365
self:EventDispatcher():RemoveEvent(self, EventID)
return self
end

View File

@@ -1,5 +1,7 @@
Utilities/Routines.lua
Utilities/Utils.lua
Utilities/Enums.lua
Utilities/Profiler.lua
Core/Base.lua
Core/UserFlag.lua
@@ -20,6 +22,7 @@ Core/Fsm.lua
Core/Radio.lua
Core/Spawn.lua
Core/SpawnStatic.lua
Core/Timer.lua
Core/Goal.lua
Core/Spot.lua
@@ -33,6 +36,7 @@ Wrapper/Client.lua
Wrapper/Static.lua
Wrapper/Airbase.lua
Wrapper/Scenery.lua
Wrapper/Marker.lua
Cargo/Cargo.lua
Cargo/CargoUnit.lua

View File

@@ -73,19 +73,7 @@ MOOSE has a living (chat and video) community of users, beta testers and contrib
# [Please DONATE ...](https://donorbox.org/fund-github-subscriptionfor-moose)
If you appreciate this development, please support to extend the framework. The development of this framework takes a lot of time.
A small gift would help me to buy a new small laptop that I can use to extend this framework while commuting to and from work ...
Also, your donations will be saved and spent wisely to the advantage of the community!
If everyone helps with a small amount, it would be really great!
<a class="dbox-donation-button" href="https://donorbox.org/fund-github-subscriptionfor-moose" style="background:#2d81c5 url(https://raw.githubusercontent.com/FlightControl-Master/MOOSE_DOCS/master/Configuration/Donate.png) no-repeat 37px center; color: #fff;text-decoration: none;font-family: Verdana,sans-serif;display: inline-block;font-size: 16px;padding: 15px 38px 15px 75px; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; box-shadow: 0 1px 0 0 #1f5a89; text-shadow: 0 1px rgba(0, 0, 0, 0.3);" >Donate</a>
Kind regards,
FlightControl (FC)