Compare commits

..

94 Commits

Author SHA1 Message Date
Applevangelist
41e8ddea8c GROUP - change to GetUnits(n) to make it more robust, now returns first alive unit,actually. Similar changes to GetHeading() 2022-05-07 11:54:33 +02:00
Applevangelist
cc49791997 Fallout fixes 2022-05-06 11:45:11 +02:00
Applevangelist
ba4a8050ba AI Patrol - life check on route 2022-05-06 10:36:39 +02:00
Applevangelist
7c5067a59a UNIT Register - small fix for trains 2022-05-06 08:01:31 +02:00
Applevangelist
69eb920173 AI_CAP - more fallout from the dead units in the API 2022-05-05 17:40:00 +02:00
Applevangelist
07d761941a AI_AIR - restrict AB on RTB 2022-05-05 16:41:32 +02:00
Applevangelist
ca52585759 AI/ZONE - Some fixes for units unreachable 2022-05-05 12:07:56 +02:00
Applevangelist
decc9d09f8 docu update 2022-05-05 11:34:14 +02:00
Applevangelist
466a18447c SRS - adding volume setting and a test on OS and IO available 2022-05-05 08:57:27 +02:00
Frank
27902ee107 Update Range.lua
**RANGE**
- Fixed a couple of bugs
- Added new FSM events for strafing
- Updated docs
2022-05-04 22:36:41 +02:00
Applevangelist
8a8b806362 further event related stuff not working any more 2022-05-04 18:09:56 +02:00
Applevangelist
40bb181c78 Another nil check... 2022-05-04 13:29:40 +02:00
Applevangelist
8099847e29 Fixes for DEAD event and extra nil checks 2022-05-04 10:25:50 +02:00
Applevangelist
6e8edd95ec GROUP - making GetCoordinate() a bit more resilient
POINT - slight changes to ToStringBRAANATO
2022-04-29 18:48:41 +02:00
Applevangelist
5112c9598b COORDINATE - added bogey option to COORDINATE:ToStringBRAANATO(FromCoordinate,Bogey,Spades) 2022-04-29 12:18:26 +02:00
Applevangelist
749158c086 Range added nil check 2022-04-29 12:09:57 +02:00
Applevangelist
b3d4024f21 Nicefy docs 2022-04-28 17:10:26 +02:00
Applevangelist
c283b66c1d added MESSAGE:ToUnit), altered MESSAGE:ToClient() accordingly 2022-04-28 16:58:37 +02:00
Applevangelist
3209843318 Added POSITIONABLE:MessageToSetUnit and POSITIONABLE:MessageToUnit 2022-04-28 16:55:31 +02:00
Applevangelist
d35e5cc0f7 Added USERSOUND:ToUnit 2022-04-28 16:50:59 +02:00
Applevangelist
e5eeb592a2 Enums - corrected Hawkeye, added Super_Hercules 2022-04-28 13:10:34 +02:00
Applevangelist
3d38f4d17a Enums - added a couple of names 2022-04-26 10:08:50 +02:00
Applevangelist
2d91647e0b QOL changes from DEVELOP 2022-04-25 10:36:36 +02:00
Applevangelist
cac0f30673 Update Moose.files (#1717) 2022-04-25 10:35:38 +02:00
Applevangelist
c5ecba3389 Menu cleanup for Refresh() 2022-04-24 14:05:45 +02:00
Applevangelist
e0397dff47 GROUP - overwrite GetHeight() inherited from POSITIONABLE with something that is actually working for groups 2022-04-24 13:50:07 +02:00
Applevangelist
e08fb2e972 Make BRAA heading a 3digit number 2022-04-22 13:31:58 +02:00
Applevangelist
c02ae82003 Point - added ToStringBRAANATO 2022-04-21 18:59:31 +02:00
Applevangelist
e6fc301b0d Utils Typo 2022-04-20 19:14:36 +02:00
Applevangelist
a385ed57fb ZONE - added example to Scan, some minor changes
SET_GROUP - clarified return value to be a table, not a SET
2022-04-20 14:03:18 +02:00
Applevangelist
09dafe4b1d UTILS - added BearingToCardinal, ToStringBRAANATO 2022-04-20 14:02:16 +02:00
Applevangelist
ba8505c983 FIFO 2022-04-14 15:54:38 +02:00
Applevangelist
fba359d389 LIFO/FIFO enforce unique id 2022-04-14 15:06:14 +02:00
Applevangelist
c56763b68f FIFO:HasUniqueID(UniqueID) 2022-04-14 11:11:33 +02:00
Applevangelist
03c2943545 Nicefy docs 2022-04-14 08:53:37 +02:00
Applevangelist
98039b9048 UTISL - FiFo/LiFo stacks 2022-04-14 08:12:29 +02:00
Applevangelist
5065d3b068 UTILS - added FiFo
CTLD - correct imperial hover check messages
2022-04-13 16:13:39 +02:00
Applevangelist
6828f7e262 UTILS - corrected door check for AH64 2022-04-12 08:23:18 +02:00
Applevangelist
a685f3ffbd MSRS - honor port setting and coalition. Repaired command string for .bat sound file production
Added mission slash in SOUNDFILE
2022-04-10 18:29:09 +02:00
Applevangelist
e84156d2e9 SRS - add port to docu 2022-04-10 10:18:12 +02:00
Applevangelist
0ce1c31e1c CSAR - added SRS port option 2022-04-10 09:29:22 +02:00
Applevangelist
f7e14bb60c SET - logic correction in :Remove() 2022-04-08 11:27:31 +02:00
Penecruz
7b907df816 Airboss V/STOL Case III and grading (#1708)
* Update Airboss.lua

* Fix syntax error

C
2022-04-08 07:16:23 +02:00
Frank
7f5be2829c Update Database.lua
- Template statics saved under first statics unit name so they can be found.
2022-04-04 16:50:17 +02:00
Applevangelist
fa5afae783 A2A Dispatcher - nil checks to evade dead units 2022-04-04 12:58:32 +02:00
Frank
b17507d0fa Update SpawnStatic.lua
- Fixed isCargo flag not honored.
2022-04-04 12:18:45 +02:00
Applevangelist
5ed43a3190 CTLD - build object at the 1st crate location not randomly around helo 2022-04-04 11:31:35 +02:00
Applevangelist
d2a5144a23 AIRBASE, added ["Deir_ez-Zor"] = "Deir ez-Zor", 2022-03-30 12:06:25 +02:00
Applevangelist
fb2031d7ca docu nicefy 2022-03-29 12:01:28 +02:00
Applevangelist
4a42571925 CTLD - corrected error in setting Hercules min and max drop height, added documentation 2022-03-29 08:52:33 +02:00
Applevangelist
38413625c2 AIRBASE - Add'l AB for the Channels map 2022-03-28 10:12:39 +02:00
Frank
2d544b7a98 Merge pull request #1698 from gavinedwards/airboss.hermes.include
Added Hermes section to Airboss.  Will require tuning.
2022-03-26 23:34:54 +01:00
Lt Cdr Gavin Edwards
b1e5e1840e Adding leading lines that I accidentally truncated. 2022-03-26 15:23:38 -07:00
Lt Cdr Gavin Edwards
e6f9b4a125 Added Hermes section to Airboss. Will require tuning. 2022-03-26 11:54:53 -07:00
Applevangelist
5260b2b430 And don't forget Fahrenheit 2022-03-26 14:46:52 +01:00
Applevangelist
0213bc7aef Correcting Celcius to Celsius 2022-03-26 14:43:06 +01:00
Applevangelist
ca8b0899d0 docu changes 2022-03-23 07:56:52 +01:00
Applevangelist
a1f5c0ab9b CSAR/CTLD - added type to script 2022-03-22 10:38:20 +01:00
Applevangelist
b0e3f82d27 AIRBASE - added 10 new AB names in Syria 2022-03-18 09:48:50 +01:00
Applevangelist
327ab4766b changed descriptions 2022-03-18 07:59:58 +01:00
Applevangelist
3aee8a49c1 Added CONTROLLABLE SetSpeed() and SetAltitude() 2022-03-18 07:39:48 +01:00
Applevangelist
57de0b7351 docu fixes 2022-03-16 08:45:27 +01:00
Applevangelist
4df1e310a3 CSAR - remove timer check for "open the door" message to make behaviour more realistic 2022-03-14 09:12:24 +01:00
TommyC81
802a77238a Range re-formatting and documentation re-fixing (#1691)
* Update Range.lua

Code formatting.

* RANGE - Documentation fixes.
2022-03-12 09:47:14 +01:00
Applevangelist
85a7e18fae SCORING: Corrected calc error in summary scoring functions 2022-03-11 10:18:59 +01:00
Applevangelist
26b1fd3487 Update CTLD.lua (#1692)
minor nil check
2022-03-09 10:26:16 +01:00
Applevangelist
ae7a363012 CTLD - small extra nil check in _GetUnitCargoMass(Unit) 2022-03-03 12:34:43 +01:00
Applevangelist
473362af45 CTLD - small fix for finding crates when using engineers 2022-03-03 11:02:25 +01:00
Applevangelist
cde0d09f0a CSAR - remove double class 2022-02-21 19:36:22 +01:00
Applevangelist
94f093826b SEAD - adding workaround for AGM_154 which lost target data 2022-02-21 08:36:37 +01:00
Applevangelist
84f231ea08 CSAR - added wet feet check if also using csarUsePara (no landing event triggered) 2022-02-18 08:22:47 +01:00
Applevangelist
3d9bb14713 CSAR - added "wet feet" option for a 2nd template to be used over water 2022-02-17 17:41:32 +01:00
Applevangelist
6c6cdcf763 CTLD - fix list/build side effect from adding weight limits to helos 2022-02-16 10:06:04 +01:00
Applevangelist
00c8690e61 CTLD - corrected default weight limits when using CTLD:UnitCapabilities() - was setting loadable weight to zero 2022-02-15 18:07:26 +01:00
Applevangelist
a0d492cd2d added back GROUP:GetHighestThreat() 2022-02-15 14:41:31 +01:00
Applevangelist
ba5ccc1021 CTLD:SetTroopDropZoneRadius(Radius) 2022-02-13 12:08:23 +01:00
Applevangelist
a4163017d5 CSAR - CSAR:SpawnCSARAtZone(Zone ...) - Zone can now be a ZONE object as well as a string 2022-02-08 07:49:04 +01:00
Applevangelist
7f4a5c48ec CTLD - add subcategory option, added CTLD:AddCTLDZoneFromAirbase(AirbaseName, Type, Color, Active, HasBeacon) 2022-02-08 07:47:48 +01:00
Applevangelist
9f7588b245 DETECTION - added 3 missing functions 2022-02-04 08:54:02 +01:00
Applevangelist
63cbc0c55b RANGE - added option to save target sheet 2022-02-03 10:01:48 +01:00
Applevangelist
28eb7a678c CTLD - Added Hercules support for crates, troops & vehicles loaded with the help of the ground crew and dropped from the plane. Added weight checks for loaded crates. 2022-02-03 10:00:19 +01:00
Applevangelist
a95c49915a SET - correct error in intersection 2022-02-01 08:02:51 +01:00
Applevangelist
b7adc6add6 POINT - added function to name/stop fires and smoke 2022-01-30 09:47:11 +01:00
Applevangelist
2aeebf280b AI Dispatchers - add ability to add/remove resources to/from a squad 2022-01-24 09:54:16 +01:00
Applevangelist
8ac06979f0 CTLD added color options for smoke drops, droppable beacons w/ timer 2022-01-23 11:42:16 +01:00
Applevangelist
2d4f90d5eb Added new Callsigns as per 2.7.9 2022-01-23 11:37:07 +01:00
TommyC81
d7a44a639d Update Detection.lua (#1685)
Code formatting. Fix minor typos, errors, and references in documentation.
2022-01-23 11:21:59 +01:00
Applevangelist
7bfa05f47d DETECTION - corrected call for Vec2 in zone 2022-01-19 07:52:59 +01:00
Applevangelist
c7bbb09195 Added doors check for UH-60L 2022-01-16 17:07:44 +01:00
Applevangelist
41c9c15ae5 CTLD, CSAR - added support for UH-60L 2022-01-16 11:39:19 +01:00
Applevangelist
964831becf CTLD - make container shape configureable 2022-01-15 11:34:23 +01:00
Applevangelist
e847b92cce RAT - Docu corrections 2022-01-11 15:14:40 +01:00
TommyC81
c2ecd86bb4 Minor fixes (#1684)
* Update AI_A2A_Dispatcher.lua

Minor code formatting.

* Update Airbase.lua

Code formatting.
2022-01-10 15:10:30 +01:00
Applevangelist
70d922fad6 SHort name mina AP 2022-01-04 15:10:25 +01:00
44 changed files with 12104 additions and 8657 deletions

10
.vs/VSWorkspaceState.json Normal file
View File

@@ -0,0 +1,10 @@
{
"ExpandedNodes": [
"",
"\\Moose Development",
"\\Moose Development\\Moose",
"\\Moose Development\\Moose\\Ops"
],
"SelectedNode": "\\Moose Development\\Moose\\Ops\\Airboss.lua",
"PreviewInSolutionExplorer": false
}

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

View File

@@ -1510,7 +1510,7 @@ do -- AI_A2A_DISPATCHER
local Message = "Clearing (" .. DefenderTask.Type .. ") " local Message = "Clearing (" .. DefenderTask.Type .. ") "
Message = Message .. Defender:GetName() Message = Message .. Defender:GetName()
if Target then if Target then
Message = Message .. ( Target and ( " from " .. Target.Index .. " [" .. Target.Set:Count() .. "]" ) ) or "" Message = Message .. ((Target and (" from " .. Target.Index .. " [" .. Target.Set:Count() .. "]")) or "")
end end
self:F( { Target = Message } ) self:F( { Target = Message } )
end end
@@ -1559,7 +1559,7 @@ do -- AI_A2A_DISPATCHER
local Message = "(" .. self.DefenderTasks[Defender].Type .. ") " local Message = "(" .. self.DefenderTasks[Defender].Type .. ") "
Message = Message .. Defender:GetName() Message = Message .. Defender:GetName()
Message = Message .. ( AttackerDetection and ( " target " .. AttackerDetection.Index .. " [" .. AttackerDetection.Set:Count() .. "]" ) ) or "" Message = Message .. ((AttackerDetection and (" target " .. AttackerDetection.Index .. " [" .. AttackerDetection.Set:Count() .. "]")) or "")
self:F( { AttackerDetection = Message } ) self:F( { AttackerDetection = Message } )
if AttackerDetection then if AttackerDetection then
self.DefenderTasks[Defender].Target = AttackerDetection self.DefenderTasks[Defender].Target = AttackerDetection
@@ -2653,7 +2653,7 @@ do -- AI_A2A_DISPATCHER
-- -- Let flights by default land and despawn at engine shutdown. -- -- Let flights by default land and despawn at engine shutdown.
-- A2ADispatcher:SetDefaultLandingAtEngineShutdown() -- A2ADispatcher:SetDefaultLandingAtEngineShutdown()
-- --
function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown() function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown()
self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown ) self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown )
@@ -2971,7 +2971,20 @@ do -- AI_A2A_DISPATCHER
for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do
-- We only allow to ENGAGE targets as long as the Units on both sides are balanced. -- We only allow to ENGAGE targets as long as the Units on both sides are balanced.
if AttackerCount > DefenderCount then if AttackerCount > DefenderCount then
local Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP --self:I("***** AI_A2A_DISPATCHER:CountDefendersToBeEngaged() *****\nThis is supposed to be a UNIT:")
if AIFriendly then
local classname = AIFriendly.ClassName or "No Class Name"
local unitname = AIFriendly.IdentifiableName or "No Unit Name"
--self:I("Class Name: " .. classname)
--self:I("Unit Name: " .. unitname)
--self:I({AIFriendly})
end
local Friendly = nil
if AIFriendly and AIFriendly:IsAlive() then
--self:I("AIFriendly alive, getting GROUP")
Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP
end
if Friendly and Friendly:IsAlive() then if Friendly and Friendly:IsAlive() then
-- Ok, so we have a friendly near the potential target. -- Ok, so we have a friendly near the potential target.
-- Now we need to check if the AIGroup has a Task. -- Now we need to check if the AIGroup has a Task.
@@ -3570,7 +3583,7 @@ do -- AI_A2A_DISPATCHER
end end
--- Assigns A2G AI Tasks in relation to the detected items. --- Assigns A2G AI Tasks in relation to the detected items.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2A_DISPATCHER self
function AI_A2A_DISPATCHER:Order( DetectedItem ) function AI_A2A_DISPATCHER:Order( DetectedItem )
local detection = self.Detection -- Functional.Detection#DETECTION_AREAS local detection = self.Detection -- Functional.Detection#DETECTION_AREAS
@@ -3876,7 +3889,31 @@ do
function AI_A2A_DISPATCHER:SchedulerCAP( SquadronName ) function AI_A2A_DISPATCHER:SchedulerCAP( SquadronName )
self:CAP( SquadronName ) self:CAP( SquadronName )
end end
--- Add resources to a Squadron
-- @param #AI_A2A_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to add.
function AI_A2A_DISPATCHER:AddToSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount + Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
--- Remove resources from a Squadron
-- @param #AI_A2A_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to remove.
function AI_A2A_DISPATCHER:RemoveFromSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount - Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
end end
do do

View File

@@ -4728,6 +4728,30 @@ do
local PatrolTaskType = PatrolTaskTypes[math.random(1,3)] local PatrolTaskType = PatrolTaskTypes[math.random(1,3)]
self:Patrol( SquadronName, PatrolTaskType ) self:Patrol( SquadronName, PatrolTaskType )
end end
--- Add resources to a Squadron
-- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to add.
function AI_A2G_DISPATCHER:AddToSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount + Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
--- Remove resources from a Squadron
-- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to remove.
function AI_A2G_DISPATCHER:RemoveFromSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount - Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
end end

View File

@@ -589,7 +589,9 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
self:ClearTargetDistance() self:ClearTargetDistance()
--AIGroup:ClearTasks() --AIGroup:ClearTasks()
AIGroup:OptionProhibitAfterburner(true)
local EngageRoute = {} local EngageRoute = {}
--- Calculate the target route point. --- Calculate the target route point.

View File

@@ -533,6 +533,10 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3() local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
if not TargetCoord then
self:Return()
return
end
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord ) local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )

View File

@@ -515,8 +515,8 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(

View File

@@ -428,8 +428,12 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then -- flight dead at this point
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() return self
end
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(

View File

@@ -460,7 +460,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(

View File

@@ -726,7 +726,8 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
end end
if self.Controllable:IsAlive() then local life = self.Controllable:GetLife() or 0
if self.Controllable:IsAlive() and life > 1 then
-- Determine if the AIControllable is within the PatrolZone. -- Determine if the AIControllable is within the PatrolZone.
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point. -- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
@@ -743,8 +744,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
if self.Controllable:InAir() == false then if self.Controllable:InAir() == false then
self:T( "Not in the air, finding route path within PatrolZone" ) self:T( "Not in the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then return end
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -758,8 +760,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
else else
self:T( "In the air, finding route path within PatrolZone" ) self:T( "In the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then return end
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -870,9 +873,10 @@ function AI_PATROL_ZONE:onafterRTB()
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
if not CurrentVec2 then return end
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(

View File

@@ -731,13 +731,14 @@ end
-- @param #BASE self -- @param #BASE self
-- @param DCS#Time EventTime The time stamp of the event. -- @param DCS#Time EventTime The time stamp of the event.
-- @param DCS#Object Initiator The initiating object of the event. -- @param DCS#Object Initiator The initiating object of the event.
function BASE:CreateEventCrash( EventTime, Initiator ) function BASE:CreateEventCrash( EventTime, Initiator, IniObjectCategory )
self:F( { EventTime, Initiator } ) self:F( { EventTime, Initiator } )
local Event = { local Event = {
id = world.event.S_EVENT_CRASH, id = world.event.S_EVENT_CRASH,
time = EventTime, time = EventTime,
initiator = Initiator, initiator = Initiator,
IniObjectCategory = IniObjectCategory,
} }
world.onEvent( Event ) world.onEvent( Event )

View File

@@ -757,7 +757,9 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
local StaticTemplate = UTILS.DeepCopy( StaticTemplate ) local StaticTemplate = UTILS.DeepCopy( StaticTemplate )
local StaticTemplateName = env.getValueDictByKey( StaticTemplate.name ) local StaticTemplateGroupName = env.getValueDictByKey( StaticTemplate.name )
local StaticTemplateName=StaticTemplate.units[1].name
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {} self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
@@ -765,7 +767,7 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
StaticTemplate.CoalitionID = CoalitionID StaticTemplate.CoalitionID = CoalitionID
StaticTemplate.CountryID = CountryID StaticTemplate.CountryID = CountryID
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateGroupName
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1] self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID

View File

@@ -131,8 +131,6 @@
-- * Weapon data: Certain events populate weapon information. -- * Weapon data: Certain events populate weapon information.
-- * Place data: Certain events populate place information. -- * Place data: Certain events populate place information.
-- --
-- Example code snippet:
--
-- --- This function is an Event Handling function that will be called when Tank1 is Dead. -- --- This function is an Event Handling function that will be called when Tank1 is Dead.
-- -- EventData is an EVENTDATA structure. -- -- EventData is an EVENTDATA structure.
-- -- We use the EventData.IniUnit to smoke the tank Green. -- -- We use the EventData.IniUnit to smoke the tank Green.
@@ -143,6 +141,7 @@
-- EventData.IniUnit:SmokeGreen() -- EventData.IniUnit:SmokeGreen()
-- end -- end
-- --
--
-- Find below an overview which events populate which information categories: -- Find below an overview which events populate which information categories:
-- --
-- ![Objects](..\Presentations\EVENT\Dia14.JPG) -- ![Objects](..\Presentations\EVENT\Dia14.JPG)
@@ -152,7 +151,6 @@
-- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. -- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated.
-- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. -- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event.
-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. -- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory.
--
-- Example code snippet: -- Example code snippet:
-- --
-- if Event.IniObjectCategory == Object.Category.UNIT then -- if Event.IniObjectCategory == Object.Category.UNIT then
@@ -174,6 +172,7 @@
-- @module Core.Event -- @module Core.Event
-- @image Core_Event.JPG -- @image Core_Event.JPG
--- @type EVENT --- @type EVENT
-- @field #EVENT.Events Events -- @field #EVENT.Events Events
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -195,6 +194,7 @@ world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006 world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007 world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
--- The different types of events supported by MOOSE. --- The different types of events supported by MOOSE.
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method. -- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
-- @type EVENTS -- @type EVENTS
@@ -227,13 +227,13 @@ EVENTS = {
MarkChange = world.event.S_EVENT_MARK_CHANGE, MarkChange = world.event.S_EVENT_MARK_CHANGE,
MarkRemoved = world.event.S_EVENT_MARK_REMOVED, MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
-- Moose Events -- Moose Events
NewCargo = world.event.S_EVENT_NEW_CARGO, NewCargo = world.event.S_EVENT_NEW_CARGO,
DeleteCargo = world.event.S_EVENT_DELETE_CARGO, DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
NewZone = world.event.S_EVENT_NEW_ZONE, NewZone = world.event.S_EVENT_NEW_ZONE,
DeleteZone = world.event.S_EVENT_DELETE_ZONE, DeleteZone = world.event.S_EVENT_DELETE_ZONE,
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL, NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL, DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT, RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT, PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
-- Added with DCS 2.5.6 -- Added with DCS 2.5.6
DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier
@@ -304,6 +304,8 @@ EVENTS = {
-- @field Core.ZONE#ZONE Zone The zone object. -- @field Core.ZONE#ZONE Zone The zone object.
-- @field #string ZoneName The name of the zone. -- @field #string ZoneName The name of the zone.
local _EVENTMETA = { local _EVENTMETA = {
[world.event.S_EVENT_SHOT] = { [world.event.S_EVENT_SHOT] = {
Order = 1, Order = 1,
@@ -560,6 +562,7 @@ local _EVENTMETA = {
}, },
} }
--- The Events structure --- The Events structure
-- @type EVENT.Events -- @type EVENT.Events
-- @field #number IniUnit -- @field #number IniUnit
@@ -573,11 +576,12 @@ function EVENT:New()
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
-- Add world event handler. -- Add world event handler.
self.EventHandler = world.addEventHandler( self ) self.EventHandler = world.addEventHandler(self)
return self return self
end end
--- Initializes the Events structure for the event. --- Initializes the Events structure for the event.
-- @param #EVENT self -- @param #EVENT self
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
@@ -591,7 +595,7 @@ function EVENT:Init( EventID, EventClass )
self.Events[EventID] = {} self.Events[EventID] = {}
end end
-- Each event has a sub-table of EventClasses, ordered by EventPriority. -- Each event has a subtable of EventClasses, ordered by EventPriority.
local EventPriority = EventClass:GetEventPriority() local EventPriority = EventClass:GetEventPriority()
if not self.Events[EventID][EventPriority] then if not self.Events[EventID][EventPriority] then
@@ -599,7 +603,7 @@ function EVENT:Init( EventID, EventClass )
end end
if not self.Events[EventID][EventPriority][EventClass] then if not self.Events[EventID][EventPriority][EventClass] then
self.Events[EventID][EventPriority][EventClass] = {} self.Events[EventID][EventPriority][EventClass] = {}
end end
return self.Events[EventID][EventPriority][EventClass] return self.Events[EventID][EventPriority][EventClass]
@@ -610,7 +614,7 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
-- @return #EVENT self -- @return #EVENT self
function EVENT:RemoveEvent( EventClass, EventID ) function EVENT:RemoveEvent( EventClass, EventID )
-- Debug info. -- Debug info.
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } ) self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
@@ -634,7 +638,7 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
-- @return #EVENT.Events -- @return #EVENT.Events
function EVENT:Reset( EventObject ) -- R2.1 function EVENT:Reset( EventObject ) --R2.1
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } ) self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
@@ -653,11 +657,12 @@ function EVENT:Reset( EventObject ) -- R2.1
end end
end end
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object. --- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.Base#BASE EventClass The self class object for which the events are removed. -- @param Core.Base#BASE EventClass The self class object for which the events are removed.
-- @return #EVENT self -- @return #EVENT self
function EVENT:RemoveAll( EventClass ) function EVENT:RemoveAll(EventClass)
local EventClassName = EventClass:GetClassNameAndID() local EventClassName = EventClass:GetClassNameAndID()
@@ -671,6 +676,8 @@ function EVENT:RemoveAll( EventClass )
return self return self
end end
--- Create an OnDead event handler for a group --- Create an OnDead event handler for a group
-- @param #EVENT self -- @param #EVENT self
-- @param #table EventTemplate -- @param #table EventTemplate
@@ -702,6 +709,7 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
return self return self
end 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 #EVENT self
-- @param #string UnitName The name of the UNIT. -- @param #string UnitName The name of the UNIT.
@@ -789,6 +797,7 @@ do -- OnDead
end end
do -- OnLand do -- OnLand
--- Create an OnLand event handler for a group --- Create an OnLand event handler for a group
@@ -855,7 +864,7 @@ do -- Event Creation
id = EVENTS.NewCargo, id = EVENTS.NewCargo,
time = timer.getTime(), time = timer.getTime(),
cargo = Cargo, cargo = Cargo,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -870,7 +879,7 @@ do -- Event Creation
id = EVENTS.DeleteCargo, id = EVENTS.DeleteCargo,
time = timer.getTime(), time = timer.getTime(),
cargo = Cargo, cargo = Cargo,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -885,7 +894,7 @@ do -- Event Creation
id = EVENTS.NewZone, id = EVENTS.NewZone,
time = timer.getTime(), time = timer.getTime(),
zone = Zone, zone = Zone,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -900,7 +909,7 @@ do -- Event Creation
id = EVENTS.DeleteZone, id = EVENTS.DeleteZone,
time = timer.getTime(), time = timer.getTime(),
zone = Zone, zone = Zone,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -915,11 +924,12 @@ do -- Event Creation
id = EVENTS.NewZoneGoal, id = EVENTS.NewZoneGoal,
time = timer.getTime(), time = timer.getTime(),
ZoneGoal = ZoneGoal, ZoneGoal = ZoneGoal,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a ZoneGoal Deletion Event. --- Creation of a ZoneGoal Deletion Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created. -- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
@@ -930,11 +940,12 @@ do -- Event Creation
id = EVENTS.DeleteZoneGoal, id = EVENTS.DeleteZoneGoal,
time = timer.getTime(), time = timer.getTime(),
ZoneGoal = ZoneGoal, ZoneGoal = ZoneGoal,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Wrapper.Unit#UNIT PlayerUnit. -- @param Wrapper.Unit#UNIT PlayerUnit.
@@ -944,8 +955,8 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterUnit, id = EVENTS.PlayerEnterUnit,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject(), initiator = PlayerUnit:GetDCSObject()
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -959,8 +970,8 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterAircraft, id = EVENTS.PlayerEnterAircraft,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject(), initiator = PlayerUnit:GetDCSObject()
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -972,24 +983,25 @@ end
-- @param #EVENTDATA Event Event data table. -- @param #EVENTDATA Event Event data table.
function EVENT:onEvent( Event ) function EVENT:onEvent( Event )
--- Function to handle errors.
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg ) env.info( "Error in SCHEDULER function:" .. errmsg )
if BASE.Debug ~= nil then if BASE.Debug ~= nil then
env.info( debug.traceback() ) env.info( debug.traceback() )
end end
return errmsg return errmsg
end end
-- Get event meta data. -- Get event meta data.
local EventMeta = _EVENTMETA[Event.id] local EventMeta = _EVENTMETA[Event.id]
-- Check if this is a known event? -- Check if this is a known event?
if EventMeta then if EventMeta then
if self and self.Events and self.Events[Event.id] and self.MissionEnd == false and (Event.initiator ~= nil or (Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit)) then if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then
-- Check if mission has ended.
if Event.id and Event.id == EVENTS.MissionEnd then if Event.id and Event.id == EVENTS.MissionEnd then
self.MissionEnd = true self.MissionEnd = true
end end
@@ -997,50 +1009,27 @@ function EVENT:onEvent( Event )
if Event.initiator then if Event.initiator then
Event.IniObjectCategory = Event.initiator:getCategory() Event.IniObjectCategory = Event.initiator:getCategory()
if Event.IniObjectCategory == Object.Category.UNIT then if Event.IniObjectCategory == Object.Category.STATIC then
Event.IniDCSUnit = Event.initiator ---
Event.IniDCSUnitName = Event.IniDCSUnit:getName() -- Static
Event.IniUnitName = Event.IniDCSUnitName ---
Event.IniDCSGroup = Event.IniDCSUnit:getGroup() if Event.id==31 then
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
if not Event.IniUnit then
-- Unit can be a CLIENT. Most likely this will be the case ...
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
end
Event.IniDCSGroupName = ""
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
-- if Event.IniGroup then
Event.IniGroupName = Event.IniDCSGroupName
-- end
end
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
end
if Event.IniObjectCategory == Object.Category.STATIC then
if Event.id == 31 then
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug. -- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
local ID = Event.initiator.id_ local ID=Event.initiator.id_
Event.IniDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( ID ) ) Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniCoalition = 0 Event.IniCoalition = 0
Event.IniCategory = 0 Event.IniCategory = 0
Event.IniTypeName = "Ejected Pilot" Event.IniTypeName = "Ejected Pilot"
elseif Event.id == 33 then -- ejection seat discarded elseif Event.id == 33 then -- ejection seat discarded
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
local ID = Event.initiator.id_ local ID=Event.initiator.id_
Event.IniDCSUnitName = string.format( "Ejection Seat ID %s", tostring( ID ) ) Event.IniDCSUnitName = string.format("Ejection Seat ID %s", tostring(ID))
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniCoalition = 0 Event.IniCoalition = 0
Event.IniCategory = 0 Event.IniCategory = 0
Event.IniTypeName = "Ejection Seat" Event.IniTypeName = "Ejection Seat"
else else
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
@@ -1051,9 +1040,47 @@ function EVENT:onEvent( Event )
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
end end
-- Dead events of units can be delayed and the initiator changed to a static.
-- Take care of that.
local Unit=UNIT:FindByName(Event.IniDCSUnitName)
if Unit then
Event.IniObjectCategory = Object.Category.UNIT
end
end
if Event.IniObjectCategory == Object.Category.UNIT then
---
-- Unit
---
Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
if not Event.IniUnit then
-- Unit can be a CLIENT. Most likely this will be the case ...
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
end
Event.IniDCSGroupName = Event.IniUnit and Event.IniUnit.GroupName or ""
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
Event.IniGroupName = Event.IniDCSGroupName
end
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
end end
if Event.IniObjectCategory == Object.Category.CARGO then if Event.IniObjectCategory == Object.Category.CARGO then
---
-- Cargo
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
@@ -1064,19 +1091,26 @@ function EVENT:onEvent( Event )
end end
if Event.IniObjectCategory == Object.Category.SCENERY then if Event.IniObjectCategory == Object.Category.SCENERY then
---
-- Scenery
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1! Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY"
end end
if Event.IniObjectCategory == Object.Category.BASE then if Event.IniObjectCategory == Object.Category.BASE then
---
-- Base Object
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = AIRBASE:FindByName( Event.IniDCSUnitName ) Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
@@ -1084,7 +1118,12 @@ function EVENT:onEvent( Event )
end end
if Event.target then if Event.target then
---
-- TARGET
---
-- Target category.
Event.TgtObjectCategory = Event.target:getCategory() Event.TgtObjectCategory = Event.target:getCategory()
if Event.TgtObjectCategory == Object.Category.UNIT then if Event.TgtObjectCategory == Object.Category.UNIT then
@@ -1097,9 +1136,7 @@ function EVENT:onEvent( Event )
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
-- if Event.TgtGroup then
Event.TgtGroupName = Event.TgtDCSGroupName Event.TgtGroupName = Event.TgtDCSGroupName
-- end
end end
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
@@ -1118,18 +1155,18 @@ function EVENT:onEvent( Event )
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
else else
Event.TgtDCSUnitName = string.format( "No target object for Event ID %s", tostring( Event.id ) ) Event.TgtDCSUnitName = string.format("No target object for Event ID %s", tostring(Event.id))
Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnitName = Event.TgtDCSUnitName
Event.TgtUnit = nil Event.TgtUnit = nil
Event.TgtCoalition = 0 Event.TgtCoalition = 0
Event.TgtCategory = 0 Event.TgtCategory = 0
if Event.id == 6 then if Event.id == 6 then
Event.TgtTypeName = "Ejected Pilot" Event.TgtTypeName = "Ejected Pilot"
Event.TgtDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( Event.IniDCSUnitName ) ) Event.TgtDCSUnitName = string.format("Ejected Pilot ID %s", tostring(Event.IniDCSUnitName))
Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnitName = Event.TgtDCSUnitName
elseif Event.id == 33 then elseif Event.id == 33 then
Event.TgtTypeName = "Ejection Seat" Event.TgtTypeName = "Ejection Seat"
Event.TgtDCSUnitName = string.format( "Ejection Seat ID %s", tostring( Event.IniDCSUnitName ) ) Event.TgtDCSUnitName = string.format("Ejection Seat ID %s", tostring(Event.IniDCSUnitName))
Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnitName = Event.TgtDCSUnitName
else else
Event.TgtTypeName = "Static" Event.TgtTypeName = "Static"
@@ -1147,6 +1184,7 @@ function EVENT:onEvent( Event )
end end
end end
-- Weapon.
if Event.weapon then if Event.weapon then
Event.Weapon = Event.weapon Event.Weapon = Event.weapon
Event.WeaponName = Event.Weapon:getTypeName() Event.WeaponName = Event.Weapon:getTypeName()
@@ -1155,49 +1193,48 @@ function EVENT:onEvent( Event )
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
-- Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
end end
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase. -- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
if Event.place then if Event.place then
if Event.id == EVENTS.LandingAfterEjection then if Event.id==EVENTS.LandingAfterEjection then
-- Place is here the UNIT of which the pilot ejected. -- Place is here the UNIT of which the pilot ejected.
-- local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :( --local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground. -- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
-- Event.Place=UNIT:Find(Event.place) --Event.Place=UNIT:Find(Event.place)
else else
Event.Place = AIRBASE:Find( Event.place ) Event.Place=AIRBASE:Find(Event.place)
Event.PlaceName = Event.Place:GetName() Event.PlaceName=Event.Place:GetName()
end end
end end
-- Mark points. -- Mark points.
if Event.idx then if Event.idx then
Event.MarkID = Event.idx Event.MarkID=Event.idx
Event.MarkVec3 = Event.pos Event.MarkVec3=Event.pos
Event.MarkCoordinate = COORDINATE:NewFromVec3( Event.pos ) Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
Event.MarkText = Event.text Event.MarkText=Event.text
Event.MarkCoalition = Event.coalition Event.MarkCoalition=Event.coalition
Event.MarkGroupID = Event.groupID Event.MarkGroupID = Event.groupID
end end
-- Cargo object.
if Event.cargo then if Event.cargo then
Event.Cargo = Event.cargo Event.Cargo = Event.cargo
Event.CargoName = Event.cargo.Name Event.CargoName = Event.cargo.Name
end end
-- Zone object.
if Event.zone then if Event.zone then
Event.Zone = Event.zone Event.Zone = Event.zone
Event.ZoneName = Event.zone.ZoneName Event.ZoneName = Event.zone.ZoneName
end end
-- Priority order.
local PriorityOrder = EventMeta.Order local PriorityOrder = EventMeta.Order
local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityBegin = PriorityOrder == -1 and 5 or 1
local PriorityEnd = PriorityOrder == -1 and 1 or 5 local PriorityEnd = PriorityOrder == -1 and 1 or 5
if Event.IniObjectCategory ~= Object.Category.STATIC then
self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
end
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
@@ -1206,12 +1243,12 @@ function EVENT:onEvent( Event )
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
-- if Event.IniObjectCategory ~= Object.Category.STATIC then --if Event.IniObjectCategory ~= Object.Category.STATIC then
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
-- end --end
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.IniGroup = Event.IniGroup or GROUP:FindByName( Event.IniDCSGroupName )
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = Event.TgtGroup or GROUP:FindByName( Event.TgtDCSGroupName )
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT. -- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
if EventData.EventUnit then if EventData.EventUnit then
@@ -1221,39 +1258,35 @@ function EVENT:onEvent( Event )
Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.PlayerEnterUnit or
Event.id == EVENTS.Crash or Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead or Event.id == EVENTS.Dead or
Event.id == EVENTS.RemoveUnit then Event.id == EVENTS.RemoveUnit or
Event.id == EVENTS.UnitLost then
local UnitName = EventClass:GetName() local UnitName = EventClass:GetName()
if (EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName) or if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
(EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName) then ( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) function()
end return EventData.EventFunction( EventClass, Event )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventData.EventFunction( EventClass, Event )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[EventMeta.Event] local EventFunction = EventClass[ EventMeta.Event ]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end return EventFunction( EventClass, Event )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventFunction( EventClass, Event )
end, ErrorHandler )
end end
end end
end end
else else
@@ -1271,48 +1304,43 @@ function EVENT:onEvent( Event )
Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.PlayerEnterUnit or
Event.id == EVENTS.Crash or Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead or Event.id == EVENTS.Dead or
Event.id == EVENTS.RemoveUnit then Event.id == EVENTS.RemoveUnit or
Event.id == EVENTS.UnitLost then
-- We can get the name of the EventClass, which is now always a GROUP object. -- We can get the name of the EventClass, which is now always a GROUP object.
local GroupName = EventClass:GetName() local GroupName = EventClass:GetName()
if (EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName) or if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
(EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName) then ( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) function()
end return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[EventMeta.Event] local EventFunction = EventClass[ EventMeta.Event ]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end return EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
end end
end end
end end
else else
-- The EventClass is not alive anymore, we remove it from the EventHandlers... -- The EventClass is not alive anymore, we remove it from the EventHandlers...
-- self:RemoveEvent( EventClass, Event.id ) --self:RemoveEvent( EventClass, Event.id )
end end
else else
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
if not EventData.EventUnit then if not EventData.EventUnit then
@@ -1321,28 +1349,25 @@ function EVENT:onEvent( Event )
if EventData.EventFunction then if EventData.EventFunction then
-- There is an EventFunction defined, so call the EventFunction. -- There is an EventFunction defined, so call the EventFunction.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end return EventData.EventFunction( EventClass, Event )
local Result, Value = xpcall( function() end, ErrorHandler )
return EventData.EventFunction( EventClass, Event )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[EventMeta.Event] local EventFunction = EventClass[ EventMeta.Event ]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end local Result, Value = EventFunction( EventClass, Event )
return Result, Value
local Result, Value = xpcall( function() end, ErrorHandler )
local Result, Value = EventFunction( EventClass, Event )
return Result, Value
end, ErrorHandler )
end end
end end
end end
@@ -1365,7 +1390,7 @@ function EVENT:onEvent( Event )
self:T( { EventMeta.Text, Event } ) self:T( { EventMeta.Text, Event } )
end end
else else
self:E( string.format( "WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring( Event.id ) ) ) self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id)))
end end
Event = nil Event = nil

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,8 @@
-- * Send message to all players. -- * Send message to all players.
-- * Send messages to a coalition. -- * Send messages to a coalition.
-- * Send messages to a specific group. -- * Send messages to a specific group.
-- * Send messages to a specific unit or client.
--
-- --
-- === -- ===
-- --
@@ -35,6 +37,7 @@
-- --
-- * To a @{Client} using @{#MESSAGE.ToClient}(). -- * To a @{Client} using @{#MESSAGE.ToClient}().
-- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}() -- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}()
-- * To a @{Wrapper.Unit} using @{#MESSAGE.ToUnit}()
-- * To a coalition using @{#MESSAGE.ToCoalition}(). -- * To a coalition using @{#MESSAGE.ToCoalition}().
-- * To the red coalition using @{#MESSAGE.ToRed}(). -- * To the red coalition using @{#MESSAGE.ToRed}().
-- * To the blue coalition using @{#MESSAGE.ToBlue}(). -- * To the blue coalition using @{#MESSAGE.ToBlue}().
@@ -194,18 +197,21 @@ function MESSAGE:ToClient( Client, Settings )
if Client and Client:GetClientGroupID() then if Client and Client:GetClientGroupID() then
if self.MessageType then if self.MessageType then
local Settings = Settings or (Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType ) self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": " self.MessageCategory = "" -- self.MessageType .. ": "
end end
local Unit = Client:GetClient()
if self.MessageDuration ~= 0 then if self.MessageDuration ~= 0 then
local ClientGroupID = Client:GetClientGroupID() local ClientGroupID = Client:GetClientGroupID()
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration ) self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) --trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
end end
end end
return self return self
end end
@@ -232,6 +238,31 @@ function MESSAGE:ToGroup( Group, Settings )
return self return self
end end
--- Sends a MESSAGE to a Unit.
-- @param #MESSAGE self
-- @param Wrapper.Unit#UNIT Unit to which the message is displayed.
-- @return #MESSAGE Message object.
function MESSAGE:ToUnit( Unit, Settings )
self:F( Unit.IdentifiableName )
if Unit then
if self.MessageType then
local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
end
end
return self
end
--- Sends a MESSAGE to the Blue coalition. --- Sends a MESSAGE to the Blue coalition.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE

File diff suppressed because it is too large Load Diff

View File

@@ -201,7 +201,7 @@ do -- SET_BASE
self:F2( { ObjectName = ObjectName } ) self:F2( { ObjectName = ObjectName } )
local TriggerEvent = true local TriggerEvent = true
if NoTriggerEvent == false then if NoTriggerEvent then
TriggerEvent = false TriggerEvent = false
end end
@@ -317,7 +317,7 @@ do -- SET_BASE
for _, Object in pairs( union.Set ) do for _, Object in pairs( union.Set ) do
if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then
intersection:AddObject( intersection ) intersection:AddObject( Object )
end end
end end
@@ -1004,7 +1004,7 @@ do -- SET_GROUP
--- Gets the Set. --- Gets the Set.
-- @param #SET_GROUP self -- @param #SET_GROUP self
-- @return #SET_GROUP self -- @return #table Table of objects
function SET_GROUP:GetAliveSet() function SET_GROUP:GetAliveSet()
self:F2() self:F2()
@@ -1064,12 +1064,17 @@ do -- SET_GROUP
function SET_GROUP:AddGroup( group ) function SET_GROUP:AddGroup( group )
self:Add( group:GetName(), group ) self:Add( group:GetName(), group )
-- I set the default cargo bay weight limit each time a new group is added to the set. if not DontSetCargoBayLimit then
for UnitID, UnitData in pairs( group:GetUnits() ) do -- I set the default cargo bay weight limit each time a new group is added to the set.
UnitData:SetCargoBayWeightLimit() -- TODO Why is this here in the first place?
for UnitID, UnitData in pairs( group:GetUnits() ) do
if UnitData and UnitData:IsAlive() then
UnitData:SetCargoBayWeightLimit()
end
end
end end
return self return self
end end
@@ -1327,7 +1332,11 @@ do -- SET_GROUP
if Event.IniDCSUnit then if Event.IniDCSUnit then
local ObjectName, Object = self:FindInDatabase( Event ) local ObjectName, Object = self:FindInDatabase( Event )
if ObjectName then if ObjectName then
if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed. local size = 1
if Event.IniDCSGroup then
size = Event.IniDCSGroup:getSize()
end
if size == 1 then -- Only remove if the last unit of the group was destroyed.
self:Remove( ObjectName ) self:Remove( ObjectName )
end end
end end

View File

@@ -2827,21 +2827,40 @@ end
-- The method will search for a #-mark, and will return the text before the #-mark. -- The method will search for a #-mark, and will return the text before the #-mark.
-- It will return nil of no prefix was found. -- It will return nil of no prefix was found.
-- @param #SPAWN self -- @param #SPAWN self
-- @param DCS#UNIT DCSUnit The @{DCSUnit} to be searched. -- @param Wrapper.Group#GROUP SpawnGroup The GROUP object.
-- @return #string The prefix -- @return #string The prefix or #nil if nothing was found.
-- @return #nil Nothing found
function SPAWN:_GetPrefixFromGroup( SpawnGroup ) function SPAWN:_GetPrefixFromGroup( SpawnGroup )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
local GroupName = SpawnGroup:GetName() local GroupName = SpawnGroup:GetName()
if GroupName then if GroupName then
local SpawnPrefix = string.match( GroupName, ".*#" )
if SpawnPrefix then local SpawnPrefix=self:_GetPrefixFromGroupName(GroupName)
SpawnPrefix = SpawnPrefix:sub( 1, -2 )
end
return SpawnPrefix return SpawnPrefix
end end
return nil
end
--- Return the prefix of a spawned group.
-- The method will search for a `#`-mark, and will return the text before the `#`-mark. It will return nil of no prefix was found.
-- @param #SPAWN self
-- @param #string SpawnGroupName The name of the spawned group.
-- @return #string The prefix or #nil if nothing was found.
function SPAWN:_GetPrefixFromGroupName(SpawnGroupName)
if SpawnGroupName then
local SpawnPrefix=string.match(SpawnGroupName, ".*#")
if SpawnPrefix then
SpawnPrefix = SpawnPrefix:sub(1, -2)
end
return SpawnPrefix
end
return nil return nil
end end
@@ -3235,24 +3254,27 @@ function SPAWN:_OnBirth( EventData )
end end
--- Obscolete
-- @todo Need to delete this... _DATABASE does this now ...
--- @param #SPAWN self --- @param #SPAWN self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnDeadOrCrash( EventData ) function SPAWN:_OnDeadOrCrash( EventData )
self:F( self.SpawnTemplatePrefix ) self:F( self.SpawnTemplatePrefix )
local SpawnGroup = EventData.IniGroup local unit=UNIT:FindByName(EventData.IniUnitName)
if SpawnGroup then if unit then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
local EventPrefix = self:_GetPrefixFromGroupName(unit.GroupName)
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "Dead event: " .. EventPrefix } ) self:T( { "Dead event: " .. EventPrefix } )
if EventPrefix == self.SpawnTemplatePrefix or (self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix) then
self.AliveUnits = self.AliveUnits - 1 if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self:T( "Alive Units: " .. self.AliveUnits )
self.AliveUnits = self.AliveUnits - 1
self:T( "Alive Units: " .. self.AliveUnits )
end end
end end
end end
end end

View File

@@ -46,14 +46,14 @@
-- @field #number InitOffsetAngle Link offset angle in degrees. -- @field #number InitOffsetAngle Link offset angle in degrees.
-- @field #number InitStaticHeading Heading of the static. -- @field #number InitStaticHeading Heading of the static.
-- @field #string InitStaticLivery Livery for aircraft. -- @field #string InitStaticLivery Livery for aircraft.
-- @field #string InitStaticShape Shape of teh static. -- @field #string InitStaticShape Shape of the static.
-- @field #string InitStaticType Type of the static. -- @field #string InitStaticType Type of the static.
-- @field #string InitStaticCategory Categrory of the static. -- @field #string InitStaticCategory Categrory of the static.
-- @field #string InitStaticName Name of the static. -- @field #string InitStaticName Name of the static.
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn 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 InitStaticDead Set static to be dead if true.
-- @field #boolean InitCargo If true, static can act as cargo. -- @field #boolean InitStaticCargo If true, static can act as cargo.
-- @field #number InitCargoMass Mass of cargo in kg. -- @field #number InitStaticCargoMass Mass of cargo in kg.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -260,7 +260,7 @@ end
-- @param #number Mass Mass of the cargo in kg. -- @param #number Mass Mass of the cargo in kg.
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargoMass(Mass) function SPAWNSTATIC:InitCargoMass(Mass)
self.InitCargoMass=Mass self.InitStaticCargoMass=Mass
return self return self
end end
@@ -269,7 +269,16 @@ end
-- @param #boolean IsCargo If true, this static can act as cargo. -- @param #boolean IsCargo If true, this static can act as cargo.
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargo(IsCargo) function SPAWNSTATIC:InitCargo(IsCargo)
self.InitCargo=IsCargo self.InitStaticCargo=IsCargo
return self
end
--- Initialize as dead.
-- @param #SPAWNSTATIC self
-- @param #boolean IsCargo If true, this static is dead.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitDead(IsDead)
self.InitStaticDead=IsDead
return self return self
end end
@@ -417,16 +426,16 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template.livery_id=self.InitStaticLivery Template.livery_id=self.InitStaticLivery
end end
if self.InitDead~=nil then if self.InitStaticDead~=nil then
Template.dead=self.InitDead Template.dead=self.InitStaticDead
end end
if self.InitCargo~=nil then if self.InitStaticCargo~=nil then
Template.canCargo=self.InitCargo Template.canCargo=self.InitStaticCargo
end end
if self.InitCargoMass~=nil then if self.InitStaticCargoMass~=nil then
Template.mass=self.InitCargoMass Template.mass=self.InitStaticCargoMass
end end
if self.InitLinkUnit then if self.InitLinkUnit then
@@ -479,6 +488,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- ED's dirty way to spawn FARPS. -- ED's dirty way to spawn FARPS.
Static=coalition.addGroup(CountryID, -1, TemplateGroup) Static=coalition.addGroup(CountryID, -1, TemplateGroup)
else else
self:T("Spawning Static")
self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template) Static=coalition.addStaticObject(CountryID, Template)
end end

File diff suppressed because it is too large Load Diff

View File

@@ -487,8 +487,10 @@ do -- Object
-- @field UNIT -- @field UNIT
-- @field WEAPON -- @field WEAPON
-- @field STATIC -- @field STATIC
-- @field SCENERY
-- @field BASE -- @field BASE
-- @field SCENERY
-- @field CARGO
--- @type Object.Desc --- @type Object.Desc
-- @extends #Desc -- @extends #Desc

View File

@@ -1053,7 +1053,7 @@ end
-- * `AIRBASE.Nevada.Lincoln_County` -- * `AIRBASE.Nevada.Lincoln_County`
-- * `AIRBASE.Nevada.McCarran_International_Airport` -- * `AIRBASE.Nevada.McCarran_International_Airport`
-- * `AIRBASE.Nevada.Mesquite` -- * `AIRBASE.Nevada.Mesquite`
-- * `AIRBASE.Nevada.Mina_Airport_3Q0` -- * `AIRBASE.Nevada.Mina_Airport`
-- * `AIRBASE.Nevada.Nellis_AFB` -- * `AIRBASE.Nevada.Nellis_AFB`
-- * `AIRBASE.Nevada.North_Las_Vegas` -- * `AIRBASE.Nevada.North_Las_Vegas`
-- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip` -- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`

File diff suppressed because it is too large Load Diff

View File

@@ -1289,7 +1289,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDestinationsFromZone(zone) function RAT:SetDestinationsFromZone(zone)
self:F2(zone) self:F2(zone)
@@ -1305,7 +1305,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDeparturesFromZone(zone) function RAT:SetDeparturesFromZone(zone)
self:F2(zone) self:F2(zone)

View File

@@ -91,13 +91,16 @@
-- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated. -- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated.
-- @field #boolean defaultsmokebomb If true, initialize player settings to smoke bomb. -- @field #boolean defaultsmokebomb If true, initialize player settings to smoke bomb.
-- @field #boolean autosave If true, automatically save results every X seconds. -- @field #boolean autosave If true, automatically save results every X seconds.
-- @field #number instructorfreq Frequency on which the range control transmits. -- @field #number instructorfreq Frequency on which the range control transmitts.
-- @field Sound.RadioQueue#RADIOQUEUE instructor Instructor radio queue. -- @field Sound.RadioQueue#RADIOQUEUE instructor Instructor radio queue.
-- @field #number rangecontrolfreq Frequency on which the range control transmits. -- @field #number rangecontrolfreq Frequency on which the range control transmitts.
-- @field Sound.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue. -- @field Sound.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue.
-- @field #string rangecontrolrelayname Name of relay unit. -- @field #string rangecontrolrelayname Name of relay unit.
-- @field #string instructorrelayname 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/". -- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
-- @field #boolean targetsheet If true, players can save their target sheets. Rangeboss will not work if targetsheets do not save.
-- @field #string targetpath Path where to save the target sheets.
-- @field #string targetprefix File prefix for target sheet files.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven --- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
@@ -121,7 +124,7 @@
-- --
-- Due to a DCS bug, it is not possible to directly monitor when a player enters a plane. So in a mission with client slots, it is vital that -- Due to a DCS bug, it is not possible to directly monitor when a player enters a plane. So in a mission with client slots, it is vital that
-- a player first enters as spectator or hits ESC twice and **after that** jumps into the slot of his aircraft! -- a player first enters as spectator or hits ESC twice and **after that** jumps into the slot of his aircraft!
-- If that is not done, the script is not started correctly. This can be checked by looking at the radio menus. If the mission was entered correctly, -- If that is not done, the script is not started correctly. This can be checked by looking at the radio menues. If the mission was entered correctly,
-- there should be an "On the Range" menu items in the "F10. Other..." menu. -- there should be an "On the Range" menu items in the "F10. Other..." menu.
-- --
-- # Strafe Pits -- # Strafe Pits
@@ -151,7 +154,7 @@
-- --
-- * The first parameter *targetnames* defines the target or targets. This can be a single item or a Table with the name(s) of @{Wrapper.Unit} or @{Static} objects defined in the mission editor. -- * The first parameter *targetnames* defines the target or targets. This can be a single item or a Table with the name(s) of @{Wrapper.Unit} or @{Static} objects defined in the mission editor.
-- * The (optional) parameter *goodhitrange* specifies the radius in metres around the target within which a bomb/rocket hit is considered to be "good". -- * The (optional) parameter *goodhitrange* specifies the radius in metres around the target within which a bomb/rocket hit is considered to be "good".
-- * If final (optional) parameter *randommove* can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone. -- * If final (optional) parameter "*randommove*" can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone.
-- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired. -- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired.
-- --
-- ## Adding Groups -- ## Adding Groups
@@ -230,6 +233,16 @@
-- The next time you start the mission, these results are also automatically loaded. -- The next time you start the mission, these results are also automatically loaded.
-- --
-- Strafing results are currently **not** saved. -- Strafing results are currently **not** saved.
--
-- # FSM Events
--
-- This class creates additional events that can be used by mission designers for custom reactions
--
-- * `EnterRange` when a player enters a range zone. See @{#RANGE.OnAfterEnterRange}
-- * `ExitRange` when a player leaves a range zone. See @{#RANGE.OnAfterExitRange}
-- * `Impact` on impact of a player's weapon on a bombing target. See @{#RANGE.OnAfterImpact}
-- * `RollingIn` when a player rolls in on a strafing target. See @{#RANGE.OnAfterRollingIn}
-- * `StrafeResult` when a player finishes a strafing run. See @{#RANGE.OnAfterStrafeResult}
-- --
-- # Examples -- # Examples
-- --
@@ -260,11 +273,12 @@
-- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target. -- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target.
-- GoldwaterRange:AddBombingTargets(bombtargets, 50) -- GoldwaterRange:AddBombingTargets(bombtargets, 50)
-- --
-- -- Start Range. -- -- Start range.
-- GoldwaterRange:Start() -- GoldwaterRange:Start()
-- --
-- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is (implicitly) used in this example. -- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is (implicitly) used in this example.
-- --
--
-- # Debugging -- # Debugging
-- --
-- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in -- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in
@@ -286,66 +300,69 @@
-- --
-- @field #RANGE -- @field #RANGE
RANGE = { RANGE = {
ClassName = "RANGE", ClassName = "RANGE",
Debug = false, Debug = false,
verbose = 0, verbose = 0,
id = nil, id = nil,
rangename = nil, rangename = nil,
location = nil, location = nil,
messages = true, messages = true,
rangeradius = 5000, rangeradius = 5000,
rangezone = nil, rangezone = nil,
strafeTargets = {}, strafeTargets = {},
bombingTargets = {}, bombingTargets = {},
nbombtargets = 0, nbombtargets = 0,
nstrafetargets = 0, nstrafetargets = 0,
MenuAddedTo = {}, MenuAddedTo = {},
planes = {}, planes = {},
strafeStatus = {}, strafeStatus = {},
strafePlayerResults = {}, strafePlayerResults = {},
bombPlayerResults = {}, bombPlayerResults = {},
PlayerSettings = {}, PlayerSettings = {},
dtBombtrack = 0.005, dtBombtrack = 0.005,
BombtrackThreshold = 25000, BombtrackThreshold = 25000,
Tmsg = 30, Tmsg = 30,
examinergroupname = nil, examinergroupname = nil,
examinerexclusive = nil, examinerexclusive = nil,
strafemaxalt = 914, strafemaxalt = 914,
ndisplayresult = 10, ndisplayresult = 10,
BombSmokeColor = SMOKECOLOR.Red, BombSmokeColor = SMOKECOLOR.Red,
StrafeSmokeColor = SMOKECOLOR.Green, StrafeSmokeColor = SMOKECOLOR.Green,
StrafePitSmokeColor = SMOKECOLOR.White, StrafePitSmokeColor = SMOKECOLOR.White,
illuminationminalt = 500, illuminationminalt = 500,
illuminationmaxalt = 1000, illuminationmaxalt = 1000,
scorebombdistance = 1000, scorebombdistance = 1000,
TdelaySmoke = 3.0, TdelaySmoke = 3.0,
eventmoose = true, eventmoose = true,
trackbombs = true, trackbombs = true,
trackrockets = true, trackrockets = true,
trackmissiles = true, trackmissiles = true,
defaultsmokebomb = true, defaultsmokebomb = true,
autosave = false, autosave = false,
instructorfreq = nil, instructorfreq = nil,
instructor = nil, instructor = nil,
rangecontrolfreq = nil, rangecontrolfreq = nil,
rangecontrol = nil, rangecontrol = nil,
soundpath = "Range Soundfiles/", soundpath = "Range Soundfiles/",
targetsheet = nil,
targetpath = nil,
targetprefix = nil,
} }
--- Default range parameters. --- Default range parameters.
-- @list Defaults -- @list Defaults
RANGE.Defaults = { RANGE.Defaults = {
goodhitrange = 25, -- meters goodhitrange = 25,
strafemaxalt = 914, -- meters AGL strafemaxalt = 914,
dtBombtrack = 0.005, -- seconds dtBombtrack = 0.005,
Tmsg = 30, -- seconds Tmsg = 30,
ndisplayresult = 10, ndisplayresult = 10,
rangeradius = 5000, -- meters rangeradius = 5000,
TdelaySmoke = 3.0, -- seconds TdelaySmoke = 3.0,
boxlength = 3000, -- meters boxlength = 3000,
boxwidth = 300, -- meters boxwidth = 300,
goodpass = 20, -- targethits per pass goodpass = 20,
foulline = 610, -- meters foulline = 610
} }
--- Target type, i.e. unit, static, or coordinate. --- Target type, i.e. unit, static, or coordinate.
@@ -354,9 +371,9 @@ RANGE.Defaults = {
-- @field #string STATIC Target is a static. -- @field #string STATIC Target is a static.
-- @field #string COORD Target is a coordinate. -- @field #string COORD Target is a coordinate.
RANGE.TargetType = { RANGE.TargetType = {
UNIT = "Unit", UNIT = "Unit",
STATIC = "Static", STATIC = "Static",
COORD = "Coordinate", COORD = "Coordinate"
} }
--- Player settings. --- Player settings.
@@ -393,6 +410,14 @@ RANGE.TargetType = {
-- @field #number smokepoints Number of smoke points. -- @field #number smokepoints Number of smoke points.
-- @field #number heading Heading of pit. -- @field #number heading Heading of pit.
--- Strafe status for player.
-- @type RANGE.StrafeStatus
-- @field #number hits Number of hits on target.
-- @field #number time Number of times.
-- @field #number ammo Amount of ammo.
-- @field #boolean pastfoulline If `true`, player passed foul line. Invalid pass.
-- @field #RANGE.StrafeTarget zone Strafe target.
--- Bomb target result. --- Bomb target result.
-- @type RANGE.BombResult -- @type RANGE.BombResult
-- @field #string name Name of closest target. -- @field #string name Name of closest target.
@@ -405,6 +430,13 @@ RANGE.TargetType = {
-- @field #number time Time via timer.getAbsTime() in seconds of impact. -- @field #number time Time via timer.getAbsTime() in seconds of impact.
-- @field #string date OS date. -- @field #string date OS date.
--- Strafe result.
-- @type RANGE.StrafeResult
-- @field #string player Player name.
-- @field #string airframe Aircraft type of player.
-- @field #number time Time via timer.getAbsTime() in seconds of impact.
-- @field #string date OS date.
--- Sound file data. --- Sound file data.
-- @type RANGE.Soundfile -- @type RANGE.Soundfile
-- @field #string filename Name of the file -- @field #string filename Name of the file
@@ -517,7 +549,7 @@ RANGE.MenuF10Root = nil
--- Range script version. --- Range script version.
-- @field #string version -- @field #string version
RANGE.version = "2.3.0" RANGE.version = "2.4.0"
-- TODO list: -- TODO list:
-- TODO: Verbosity level for messages. -- TODO: Verbosity level for messages.
@@ -568,6 +600,8 @@ function RANGE:New( rangename )
self:AddTransition("Stopped", "Start", "Running") -- Start RANGE script. self:AddTransition("Stopped", "Start", "Running") -- Start RANGE script.
self:AddTransition("*", "Status", "*") -- Status of RANGE script. self:AddTransition("*", "Status", "*") -- Status of RANGE script.
self:AddTransition("*", "Impact", "*") -- Impact of bomb/rocket/missile. self:AddTransition("*", "Impact", "*") -- Impact of bomb/rocket/missile.
self:AddTransition("*", "RollingIn", "*") -- Player rolling in on strafe target.
self:AddTransition("*", "StrafeResult", "*") -- Strafe result of player.
self:AddTransition("*", "EnterRange", "*") -- Player enters the range. self:AddTransition("*", "EnterRange", "*") -- Player enters the range.
self:AddTransition("*", "ExitRange", "*") -- Player leaves the range. self:AddTransition("*", "ExitRange", "*") -- Player leaves the range.
self:AddTransition("*", "Save", "*") -- Save player results. self:AddTransition("*", "Save", "*") -- Save player results.
@@ -625,6 +659,37 @@ function RANGE:New( rangename )
-- @param #RANGE.BombResult result Data of the bombing run. -- @param #RANGE.BombResult result Data of the bombing run.
-- @param #RANGE.PlayerData player Data of player settings etc. -- @param #RANGE.PlayerData player Data of player settings etc.
--- Triggers the FSM event "RollingIn".
-- @function [parent=#RANGE] RollingIn
-- @param #RANGE self
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeTarget target Strafe target.
--- On after "RollingIn" event user function. Called when a player rolls in to a strafe taret.
-- @function [parent=#RANGE] OnAfterRollingIn
-- @param #RANGE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeTarget target Strafe target.
--- Triggers the FSM event "StrafeResult".
-- @function [parent=#RANGE] StrafeResult
-- @param #RANGE self
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeResult result Data of the strafing run.
--- On after "StrafeResult" event user function. Called when a player finished a strafing run.
-- @function [parent=#RANGE] OnAfterStrafeResult
-- @param #RANGE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeResult result Data of the strafing run.
--- Triggers the FSM event "EnterRange". --- Triggers the FSM event "EnterRange".
-- @function [parent=#RANGE] EnterRange -- @function [parent=#RANGE] EnterRange
-- @param #RANGE self -- @param #RANGE self
@@ -869,6 +934,22 @@ function RANGE:SetAutosaveOff()
return self return self
end end
--- Enable saving of player's target sheets and specify an optional directory path.
-- @param #RANGE self
-- @param #string path (Optional) Path where to save the target sheets.
-- @param #string prefix (Optional) Prefix for target sheet files. File name will be saved as *prefix_aircrafttype-0001.csv*, *prefix_aircrafttype-0002.csv*, etc.
-- @return #RANGE self
function RANGE:SetTargetSheet( path, prefix )
if io then
self.targetsheet = true
self.targetpath = path
self.targetprefix = prefix
else
self:E( self.lid .. "ERROR: io is not desanitized. Cannot save target sheet." )
end
return self
end
--- Set messages to examiner. The examiner will receive messages from all clients. --- Set messages to examiner. The examiner will receive messages from all clients.
-- @param #RANGE self -- @param #RANGE self
-- @param #string examinergroupname Name of the group of the examiner. -- @param #string examinergroupname Name of the group of the examiner.
@@ -900,10 +981,10 @@ end
--- Set player setting whether bomb impact points are smoked or not. --- Set player setting whether bomb impact points are smoked or not.
-- @param #RANGE self -- @param #RANGE self
-- @param #boolean switch (Optional) If true, impact points of bombs will be smoked. Default is true. -- @param #boolean switch If true nor nil default is to smoke impact points of bombs.
-- @return #RANGE self -- @return #RANGE self
function RANGE:SetDefaultPlayerSmokeBomb( switch ) function RANGE:SetDefaultPlayerSmokeBomb( switch )
if switch == nil or switch == true then if switch == true or switch == nil then
self.defaultsmokebomb = true self.defaultsmokebomb = true
else else
self.defaultsmokebomb = false self.defaultsmokebomb = false
@@ -1183,7 +1264,7 @@ function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseh
if heading < 0 then if heading < 0 then
heading = heading + 360 heading = heading + 360
end end
if heading >= 360 then if heading > 360 then
heading = heading - 360 heading = heading - 360
end end
@@ -1246,7 +1327,7 @@ end
-- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m. -- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m.
-- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m. -- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m.
-- @param #number heading (Optional) Approach heading in Degrees. Default is heading of the unit as defined in the mission editor. -- @param #number heading (Optional) Approach heading in Degrees. Default is heading of the unit as defined in the mission editor.
-- @param #boolean inverseheading (Optional) Use inverse heading (heading --> heading - 180 Degrees). Default is false. -- @param #boolean inverseheading (Optional) Take inverse heading (heading --> heading - 180 Degrees). Default is false.
-- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20. -- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20.
-- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default 610 m = 2000 ft. Set to 0 for no foul line. -- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default 610 m = 2000 ft. Set to 0 for no foul line.
-- @return #RANGE self -- @return #RANGE self
@@ -1281,8 +1362,8 @@ end
--- Add bombing target(s) to range. --- Add bombing target(s) to range.
-- @param #RANGE self -- @param #RANGE self
-- @param #table targetnames Single or multiple (Table) names of unit or static objects serving as bomb targets. -- @param #table targetnames Single or multiple (Table) names of unit or static objects serving as bomb targets.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange (Optional) Max distance from target unit (in meters) which is considered as a good hit. Default is 25 m.
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false. -- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
-- @return #RANGE self -- @return #RANGE self
function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove ) function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove )
self:F( { targetnames = targetnames, goodhitrange = goodhitrange, randommove = randommove } ) self:F( { targetnames = targetnames, goodhitrange = goodhitrange, randommove = randommove } )
@@ -1320,8 +1401,8 @@ end
--- Add a unit or static object as bombing target. --- Add a unit or static object as bombing target.
-- @param #RANGE self -- @param #RANGE self
-- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the strafe target. -- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the strafe target.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange Max distance from unit which is considered as a good hit.
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false. -- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
-- @return #RANGE self -- @return #RANGE self
function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove ) function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
self:F( { unit = unit, goodhitrange = goodhitrange, randommove = randommove } ) self:F( { unit = unit, goodhitrange = goodhitrange, randommove = randommove } )
@@ -1374,25 +1455,12 @@ function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
return self return self
end end
--- Add a coordinate of a bombing target. --- Add a coordinate of a bombing target. This
-- @param #RANGE self -- @param #RANGE self
-- @param Core.Point#COORDINATE coord The coordinate. -- @param Core.Point#COORDINATE coord The coordinate.
-- @param #string name (Optional) Name of target. Default is "Bomb Target". -- @param #string name Name of target.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange Max distance from unit which is considered as a good hit.
-- @return #RANGE self -- @return #RANGE self
-- @usage
--
-- -- Setup a Range
-- RangeOne = RANGE:New( "Range One" )
-- -- Find the STATIC target object as setup in the ME.
-- RangeOneBombTarget = STATIC:FindByName( "RangeOneBombTarget" )
-- -- Add the coordinate of the STATIC target object as a bomb target (thus keeping the bomb function active, even if the STATIC target is destroyed).
-- RangeOne:AddBombingTargetCoordinate( RangeOneBombTarget:GetCoordinate(), "RangeOneBombTarget", 50)
-- -- Or, add the coordinate of the STATIC target object as a bomb target using default values (name will be "Bomb Target", goodhitrange will be 25 m).
-- RangeOne:AddBombingTargetCoordinate( RangeOneBombTarget:GetCoordinate() )
-- -- Start Range.
-- RangeOne:Start()
--
function RANGE:AddBombingTargetCoordinate( coord, name, goodhitrange ) function RANGE:AddBombingTargetCoordinate( coord, name, goodhitrange )
local target = {} -- #RANGE.BombTarget local target = {} -- #RANGE.BombTarget
@@ -1413,8 +1481,8 @@ end
--- Add all units of a group as bombing targets. --- Add all units of a group as bombing targets.
-- @param #RANGE self -- @param #RANGE self
-- @param Wrapper.Group#GROUP group Group of bombing targets. -- @param Wrapper.Group#GROUP group Group of bombing targets.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange Max distance from unit which is considered as a good hit.
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false. -- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
-- @return #RANGE self -- @return #RANGE self
function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove ) function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove )
self:F( { group = group, goodhitrange = goodhitrange, randommove = randommove } ) self:F( { group = group, goodhitrange = goodhitrange, randommove = randommove } )
@@ -1433,22 +1501,11 @@ function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove )
return self return self
end end
--- Returns the foul line distance between strafe pit target and a foul line distance marker object. --- Measures the foule line distance between two unit or static objects.
-- @param #RANGE self -- @param #RANGE self
-- @param #string namepit Name of the strafe pit target object. -- @param #string namepit Name of the strafe pit target object.
-- @param #string namefoulline Name of the foul line distance marker object. -- @param #string namefoulline Name of the fould line distance marker object.
-- @return #number Foul line distance in meters. -- @return #number Foul line distance in meters.
-- @usage
--
-- -- Setup a Range
-- RangeOne = RANGE:New( "Range One" )
-- -- Get distance between strafe target objext and foul line distance marker object.
-- RangeOneFoulDistance = RangeOne:GetFoullineDistance( "RangeOneStrafeTarget" , "RangeOneFoulLineObject" )
-- -- Add a strafe pit using the measured foul line distance. Where nil is used, strafe pit default values will be used - adjust as required.
-- RangeOne:AddStrafePit( "RangeOneStrafeTarget", nil, nil, nil, nil, nil, RangeOneFoulDistance )
-- -- Start Range.
-- RangeOne:Start()
--
function RANGE:GetFoullineDistance( namepit, namefoulline ) function RANGE:GetFoullineDistance( namepit, namefoulline )
self:F( { namepit = namepit, namefoulline = namefoulline } ) self:F( { namepit = namepit, namefoulline = namefoulline } )
@@ -1573,6 +1630,7 @@ function RANGE:OnEventBirth( EventData )
self:T3( self.id .. "BIRTH: player = " .. tostring( _playername ) ) self:T3( self.id .. "BIRTH: player = " .. tostring( _playername ) )
if _unit and _playername then if _unit and _playername then
local _uid = _unit:GetID() local _uid = _unit:GetID()
local _group = _unit:GetGroup() local _group = _unit:GetGroup()
local _gid = _group:GetID() local _gid = _group:GetID()
@@ -1586,7 +1644,6 @@ function RANGE:OnEventBirth( EventData )
self.strafeStatus[_uid] = nil self.strafeStatus[_uid] = nil
-- Add Menu commands after a delay of 0.1 seconds. -- Add Menu commands after a delay of 0.1 seconds.
-- SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName ) self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
-- By default, some bomb impact points and do not flare each hit on target. -- By default, some bomb impact points and do not flare each hit on target.
@@ -1605,12 +1662,11 @@ function RANGE:OnEventBirth( EventData )
-- Start check in zone timer. -- Start check in zone timer.
if self.planes[_uid] ~= true then if self.planes[_uid] ~= true then
-- SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1)
self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 ) self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 )
self.planes[_uid] = true self.planes[_uid] = true
end end
end
end
end end
--- Range event handler for event hit. --- Range event handler for event hit.
@@ -1639,7 +1695,7 @@ function RANGE:OnEventHit( EventData )
local targetname = EventData.TgtUnitName local targetname = EventData.TgtUnitName
-- Current strafe target of player. -- Current strafe target of player.
local _currentTarget = self.strafeStatus[_unitID] local _currentTarget = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
-- Player has rolled in on a strafing target. -- Player has rolled in on a strafing target.
if _currentTarget and target:IsAlive() then if _currentTarget and target:IsAlive() then
@@ -1672,6 +1728,7 @@ function RANGE:OnEventHit( EventData )
self:_DisplayMessageToGroup( _unit, text ) self:_DisplayMessageToGroup( _unit, text )
self:T2( self.id .. text ) self:T2( self.id .. text )
_currentTarget.pastfoulline = true _currentTarget.pastfoulline = true
invalidStrafe = true -- Rangeboss Edit
end end
end end
@@ -1701,7 +1758,6 @@ function RANGE:OnEventHit( EventData )
end end
end end
end end
end end
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun). --- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
@@ -1762,6 +1818,7 @@ function RANGE:OnEventShot( EventData )
-- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons. -- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons.
if _track and dPR <= self.BombtrackThreshold and _unit and _playername then if _track and dPR <= self.BombtrackThreshold and _unit and _playername then
-- Player data. -- Player data.
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
@@ -1843,7 +1900,9 @@ function RANGE:OnEventShot( EventData )
_distance = _temp _distance = _temp
_closetTarget = _bombtarget _closetTarget = _bombtarget
_closeCoord = targetcoord _closeCoord = targetcoord
if _distance <= 0.5 * _bombtarget.goodhitrange then if _distance <= 1.53 then -- Rangeboss Edit
_hitquality = "SHACK" -- Rangeboss Edit
elseif _distance <= 0.5 * _bombtarget.goodhitrange then -- Rangeboss Edit
_hitquality = "EXCELLENT" _hitquality = "EXCELLENT"
elseif _distance <= _bombtarget.goodhitrange then elseif _distance <= _bombtarget.goodhitrange then
_hitquality = "GOOD" _hitquality = "GOOD"
@@ -1876,6 +1935,10 @@ function RANGE:OnEventShot( EventData )
result.player = playerData.playername result.player = playerData.playername
result.time = timer.getAbsTime() result.time = timer.getAbsTime()
result.airframe = playerData.airframe result.airframe = playerData.airframe
result.roundsFired = 0 -- Rangeboss Edit
result.roundsHit = 0 -- Rangeboss Edit
result.roundsQuality = "N/A" -- Rangeboss Edit
result.rangename = self.rangename
-- Add to table. -- Add to table.
table.insert( _results, result ) table.insert( _results, result )
@@ -2012,11 +2075,16 @@ end
-- @param #RANGE.PlayerData player Player data table. -- @param #RANGE.PlayerData player Player data table.
function RANGE:onafterImpact( From, Event, To, result, player ) function RANGE:onafterImpact( From, Event, To, result, player )
-- Send message to player.
local text = string.format( "%s, impact %03d° for %d m (%d ft)", player.playername, result.radial, result.distance, UTILS.MetersToFeet( result.distance ) )
-- Only display target name if there is more than one bomb target. -- Only display target name if there is more than one bomb target.
local targetname = nil
if #self.bombingTargets > 1 then if #self.bombingTargets > 1 then
text = text .. string.format( " from bulls of target %s.", result.name ) local targetname = result.name
end
-- Send message to player.
local text = string.format( "%s, impact %03d° for %d ft", player.playername, result.radial, UTILS.MetersToFeet( result.distance ) )
if targetname then
text = text .. string.format( " from bulls of target %s." )
else else
text = text .. "." text = text .. "."
end end
@@ -2120,7 +2188,7 @@ function RANGE:onafterSave( From, Event, To )
_savefile( filename, scores ) _savefile( filename, scores )
end end
--- Function called before load event. Checks that io and lfs are desanitized. --- Function called before save event. Checks that io and lfs are desanitized.
-- @param #RANGE self -- @param #RANGE self
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
@@ -2211,6 +2279,73 @@ function RANGE:onafterLoad( From, Event, To )
end end
end end
--- Save target sheet.
-- @param #RANGE self
-- @param #string _playername Player name.
-- @param #RANGE.StrafeResult result Results table.
function RANGE:_SaveTargetSheet( _playername, result ) -- RangeBoss Specific Function
--- Function that saves data to file
local function _savefile( filename, data )
local f = io.open( filename, "wb" )
if f then
f:write( data )
f:close()
else
env.info( "RANGEBOSS EDIT - could not save target sheet to file" )
-- self:E(self.lid..string.format("ERROR: could not save target sheet to file %s.\nFile may contain invalid characters.", tostring(filename)))
end
end
-- Set path or default.
local path = self.targetpath
if lfs then
path = path or lfs.writedir() .. [[Logs\]]
end
-- Create unused file name.
local filename = nil
for i = 1, 9999 do
-- Create file name
if self.targetprefix then
filename = string.format( "%s_%s-%04d.csv", self.targetprefix, result.airframe, i )
else
local name = UTILS.ReplaceIllegalCharacters( _playername, "_" )
filename = string.format( "RANGERESULTS-%s_Targetsheet-%s-%04d.csv", self.rangename, name, i )
end
-- Set path.
if path ~= nil then
filename = path .. "\\" .. filename
end
-- Check if file exists.
local _exists = UTILS.FileExists( filename )
if not _exists then
break
end
end
-- Header line
local data = "Name,Target,Rounds Fired,Rounds Hit,Rounds Quality,Airframe,Mission Time,OS Time\n"
local target = result.name
local airframe = result.airframe
local roundsFired = result.roundsFired
local roundsHit = result.roundsHit
local strafeResult = result.roundsQuality
local time = UTILS.SecondsToClock( result.time )
local date = "n/a"
if os then
date = os.date()
end
data = data .. string.format( "%s,%s,%d,%d,%s,%s,%s,%s", _playername, target, roundsFired, roundsHit, strafeResult, airframe, time, date )
-- Save file.
_savefile( filename, data )
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Display Messages -- Display Messages
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2236,7 +2371,7 @@ function RANGE:_DisplayMyStrafePitResults( _unitName )
local _message = string.format( "My Top %d Strafe Pit Results:\n", self.ndisplayresult ) local _message = string.format( "My Top %d Strafe Pit Results:\n", self.ndisplayresult )
-- Get player results. -- Get player results.
local _results = self.strafePlayerResults[_playername] local _results = self.strafePlayerResults[_playername]
-- Create message. -- Create message.
if _results == nil then if _results == nil then
@@ -2256,9 +2391,10 @@ function RANGE:_DisplayMyStrafePitResults( _unitName )
-- Loop over results -- Loop over results
for _, _result in pairs( _results ) do for _, _result in pairs( _results ) do
local result=_result --#RANGE.StrafeResult
-- Message text. -- Message text.
_message = _message .. string.format( "\n[%d] Hits %d - %s - %s", _count, _result.hits, _result.zone.name, _result.text ) _message = _message .. string.format( "\n[%d] Hits %d - %s - %s", _count, result.roundsHit, result.name, result.roundsQuality )
-- Best result. -- Best result.
if _bestMsg == "" then if _bestMsg == "" then
@@ -2489,7 +2625,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
local range = coord:Get2DDistance( position ) local range = coord:Get2DDistance( position )
-- Bearing string. -- Bearing string.
local Bs = string.format( "%03d°", angle ) local Bs = string.format( '%03d°', angle )
local texthit local texthit
if self.PlayerSettings[playername].flaredirecthits then if self.PlayerSettings[playername].flaredirecthits then
@@ -2583,7 +2719,7 @@ function RANGE:_DisplayBombTargets( _unitname )
end end
end end
self:_DisplayMessageToGroup( _unit, _text, 60, true, true ) self:_DisplayMessageToGroup( _unit, _text, 120, true, true )
end end
end end
@@ -2656,7 +2792,7 @@ function RANGE:_DisplayRangeWeather( _unitname )
-- Get Beaufort wind scale. -- Get Beaufort wind scale.
local Bn, Bd = UTILS.BeaufortScale( Ws ) local Bn, Bd = UTILS.BeaufortScale( Ws )
local WD = string.format( "%03d°", Wd ) local WD = string.format( '%03d°', Wd )
local Ts = string.format( "%d°C", T ) local Ts = string.format( "%d°C", T )
local hPa2inHg = 0.0295299830714 local hPa2inHg = 0.0295299830714
@@ -2744,9 +2880,13 @@ function RANGE:_CheckInZone( _unitName )
-- Get player unit and name. -- Get player unit and name.
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
local unitheading = 0 -- RangeBoss
if _unit and _playername then if _unit and _playername then
-- Player data.
local playerData=self.PlayerSettings[_playername] -- #RANGE.PlayerData
--- Function to check if unit is in zone and facing in the right direction and is below the max alt. --- Function to check if unit is in zone and facing in the right direction and is below the max alt.
local function checkme( targetheading, _zone ) local function checkme( targetheading, _zone )
local zone = _zone -- Core.Zone#ZONE local zone = _zone -- Core.Zone#ZONE
@@ -2760,7 +2900,7 @@ function RANGE:_CheckInZone( _unitName )
if towardspit then if towardspit then
local vec3 = _unit:GetVec3() local vec3 = _unit:GetVec3()
local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2 local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2
local landheight = land.getHeight( vec2 ) local landheight = land.getHeight( vec2 )
local unitalt = vec3.y - landheight local unitalt = vec3.y - landheight
@@ -2777,7 +2917,7 @@ function RANGE:_CheckInZone( _unitName )
local _unitID = _unit:GetID() local _unitID = _unit:GetID()
-- Currently strafing? (strafeStatus is nil if not) -- Currently strafing? (strafeStatus is nil if not)
local _currentStrafeRun = self.strafeStatus[_unitID] local _currentStrafeRun = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
if _currentStrafeRun then -- player has already registered for a strafing run. if _currentStrafeRun then -- player has already registered for a strafing run.
@@ -2789,7 +2929,6 @@ function RANGE:_CheckInZone( _unitName )
-- Check if player is in strafe zone and below max alt. -- Check if player is in strafe zone and below max alt.
if unitinzone then if unitinzone then
-- Still in zone, keep counting hits. Increase counter. -- Still in zone, keep counting hits. Increase counter.
_currentStrafeRun.time = _currentStrafeRun.time + 1 _currentStrafeRun.time = _currentStrafeRun.time + 1
@@ -2819,24 +2958,27 @@ function RANGE:_CheckInZone( _unitName )
local _ammo = self:_GetAmmo( _unitName ) local _ammo = self:_GetAmmo( _unitName )
-- Result. -- Result.
local _result = self.strafeStatus[_unitID] local _result = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
local _sound = nil -- #RANGE.Soundfile local _sound = nil -- #RANGE.Soundfile
--[[ --RangeBoss commented out in order to implement strafe quality based on accuracy percentage, not the number of rounds on target
-- Judge this pass. Text is displayed on summary. -- Judge this pass. Text is displayed on summary.
if _result.hits >= _result.zone.goodPass * 2 then if _result.hits >= _result.zone.goodPass*2 then
_result.text = "EXCELLENT PASS" _result.text = "EXCELLENT PASS"
_sound = RANGE.Sound.RCExcellentPass _sound=RANGE.Sound.RCExcellentPass
elseif _result.hits >= _result.zone.goodPass then elseif _result.hits >= _result.zone.goodPass then
_result.text = "GOOD PASS" _result.text = "GOOD PASS"
_sound = RANGE.Sound.RCGoodPass _sound=RANGE.Sound.RCGoodPass
elseif _result.hits >= _result.zone.goodPass / 2 then elseif _result.hits >= _result.zone.goodPass/2 then
_result.text = "INEFFECTIVE PASS" _result.text = "INEFFECTIVE PASS"
_sound = RANGE.Sound.RCIneffectivePass _sound=RANGE.Sound.RCIneffectivePass
else else
_result.text = "POOR PASS" _result.text = "POOR PASS"
_sound = RANGE.Sound.RCPoorPass _sound=RANGE.Sound.RCPoorPass
end end
]]
-- Calculate accuracy of run. Number of hits wrt number of rounds fired. -- Calculate accuracy of run. Number of hits wrt number of rounds fired.
local shots = _result.ammo - _ammo local shots = _result.ammo - _ammo
local accur = 0 local accur = 0
@@ -2846,6 +2988,30 @@ function RANGE:_CheckInZone( _unitName )
accur = 100 accur = 100
end end
end end
-- Results text and sound message.
local resulttext=""
if _result.pastfoulline == true then --
resulttext = "* INVALID - PASSED FOUL LINE *"
_sound = RANGE.Sound.RCPoorPass --
else
if accur >= 90 then
resulttext = "DEADEYE PASS"
_sound = RANGE.Sound.RCExcellentPass
elseif accur >= 75 then
resulttext = "EXCELLENT PASS"
_sound = RANGE.Sound.RCExcellentPass
elseif accur >= 50 then
resulttext = "GOOD PASS"
_sound = RANGE.Sound.RCGoodPass
elseif accur >= 25 then
resulttext = "INEFFECTIVE PASS"
_sound = RANGE.Sound.RCIneffectivePass
else
resulttext = "POOR PASS"
_sound = RANGE.Sound.RCPoorPass
end
end
-- Message text. -- Message text.
local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits ) local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits )
@@ -2856,6 +3022,27 @@ function RANGE:_CheckInZone( _unitName )
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _text ) self:_DisplayMessageToGroup( _unit, _text )
-- Strafe result.
local result = {} -- #RANGE.StrafeResult
result.player=_playername
result.name=_result.zone.name or "unknown"
result.time = timer.getAbsTime()
result.roundsFired = shots
result.roundsHit = _result.hits
result.roundsQuality = resulttext
result.strafeAccuracy = accur
result.rangename = self.rangename
result.airframe=playerData.airframe
result.invalid = _result.pastfoulline
-- Griger Results.
self:StrafeResult(playerData, result)
-- Save trap sheet.
if playerData and playerData.targeton and self.targetsheet then
self:_SaveTargetSheet( _playername, result )
end
-- Voice over. -- Voice over.
if self.rangecontrol then if self.rangecontrol then
@@ -2876,7 +3063,7 @@ function RANGE:_CheckInZone( _unitName )
-- Save stats so the player can retrieve them. -- Save stats so the player can retrieve them.
local _stats = self.strafePlayerResults[_playername] or {} local _stats = self.strafePlayerResults[_playername] or {}
table.insert( _stats, _result ) table.insert( _stats, result )
self.strafePlayerResults[_playername] = _stats self.strafePlayerResults[_playername] = _stats
end end
@@ -2886,12 +3073,13 @@ function RANGE:_CheckInZone( _unitName )
-- Check to see if we're in any of the strafing zones (first time). -- Check to see if we're in any of the strafing zones (first time).
for _, _targetZone in pairs( self.strafeTargets ) do for _, _targetZone in pairs( self.strafeTargets ) do
local target=_targetZone --#RANGE.StrafeTarget
-- Get the current approach zone and check if player is inside. -- Get the current approach zone and check if player is inside.
local zone = _targetZone.polygon -- Core.Zone#ZONE_POLYGON_BASE local zone = target.polygon -- Core.Zone#ZONE_POLYGON_BASE
-- Check if unit in zone and facing the right direction. -- Check if unit in zone and facing the right direction.
local unitinzone = checkme( _targetZone.heading, zone ) local unitinzone = checkme( target.heading, zone )
-- Player is inside zone. -- Player is inside zone.
if unitinzone then if unitinzone then
@@ -2900,10 +3088,10 @@ function RANGE:_CheckInZone( _unitName )
local _ammo = self:_GetAmmo( _unitName ) local _ammo = self:_GetAmmo( _unitName )
-- Init strafe status for this player. -- Init strafe status for this player.
self.strafeStatus[_unitID] = { hits = 0, zone = _targetZone, time = 1, ammo = _ammo, pastfoulline = false } self.strafeStatus[_unitID] = { hits = 0, zone = target, time = 1, ammo = _ammo, pastfoulline = false }
-- Rolling in! -- Rolling in!
local _msg = string.format( "%s, rolling in on strafe pit %s.", self:_myname( _unitName ), _targetZone.name ) local _msg = string.format( "%s, rolling in on strafe pit %s.", self:_myname( _unitName ), target.name )
if self.rangecontrol then if self.rangecontrol then
self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath ) self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath )
@@ -2911,6 +3099,9 @@ function RANGE:_CheckInZone( _unitName )
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _msg, 10, true ) self:_DisplayMessageToGroup( _unit, _msg, 10, true )
-- Trigger event that player is rolling in.
self:RollingIn(playerData, target)
-- We found our player. Skip remaining checks. -- We found our player. Skip remaining checks.
break break
@@ -2959,7 +3150,8 @@ function RANGE:_AddF10Commands( _unitName )
-- MISSION LEVEL -- -- MISSION LEVEL --
------------------- -------------------
_rangePath = missionCommands.addSubMenuForGroup( _gid, self.rangename, RANGE.MenuF10Root ) -- _rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10Root)
_rangePath = MENU_GROUP:New( group, "On the Range" )
else else
@@ -2969,54 +3161,57 @@ function RANGE:_AddF10Commands( _unitName )
-- Main F10 menu: F10/On the Range/<Range Name>/ -- Main F10 menu: F10/On the Range/<Range Name>/
if RANGE.MenuF10[_gid] == nil then if RANGE.MenuF10[_gid] == nil then
RANGE.MenuF10[_gid] = missionCommands.addSubMenuForGroup( _gid, "On the Range" ) -- RANGE.MenuF10[_gid]=missionCommands.addSubMenuForGroup(_gid, "On the Range")
RANGE.MenuF10[_gid] = MENU_GROUP:New( group, "On the Range" )
end end
_rangePath = missionCommands.addSubMenuForGroup( _gid, self.rangename, RANGE.MenuF10[_gid] ) -- _rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10[_gid])
_rangePath = MENU_GROUP:New( group, self.rangename, RANGE.MenuF10[_gid] )
end end
local _statsPath = missionCommands.addSubMenuForGroup( _gid, "Statistics", _rangePath ) local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
local _markPath = missionCommands.addSubMenuForGroup( _gid, "Mark Targets", _rangePath ) local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
local _settingsPath = missionCommands.addSubMenuForGroup( _gid, "My Settings", _rangePath ) local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
local _infoPath = missionCommands.addSubMenuForGroup( _gid, "Range Info", _rangePath ) local _infoPath = MENU_GROUP:New( group, "Range Info", _rangePath )
-- F10/On the Range/<Range Name>/My Settings/ -- F10/On the Range/<Range Name>/My Settings/
local _mysmokePath = missionCommands.addSubMenuForGroup( _gid, "Smoke Color", _settingsPath ) local _mysmokePath = MENU_GROUP:New( group, "Smoke Color", _settingsPath )
local _myflarePath = missionCommands.addSubMenuForGroup( _gid, "Flare Color", _settingsPath ) local _myflarePath = MENU_GROUP:New( group, "Flare Color", _settingsPath )
-- F10/On the Range/<Range Name>/Mark Targets/ -- F10/On the Range/<Range Name>/Mark Targets/
missionCommands.addCommandForGroup( _gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName ) local _MoMap = MENU_GROUP_COMMAND:New( group, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName ) local _IllRng = MENU_GROUP_COMMAND:New( group, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName ) local _SSpit = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName ) local _SStgts = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName ) local _SBtgts = MENU_GROUP_COMMAND:New( group, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName )
-- F10/On the Range/<Range Name>/Stats/ -- F10/On the Range/<Range Name>/Stats/
missionCommands.addCommandForGroup( _gid, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName ) local _AllSR = MENU_GROUP_COMMAND:New( group, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName ) local _AllBR = MENU_GROUP_COMMAND:New( group, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName ) local _MySR = MENU_GROUP_COMMAND:New( group, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName ) local _MyBR = MENU_GROUP_COMMAND:New( group, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName ) local _ResetST = MENU_GROUP_COMMAND:New( group, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName )
-- F10/On the Range/<Range Name>/My Settings/Smoke Color/ -- F10/On the Range/<Range Name>/My Settings/Smoke Color/
missionCommands.addCommandForGroup( _gid, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue ) local _BlueSM = MENU_GROUP_COMMAND:New( group, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue )
missionCommands.addCommandForGroup( _gid, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green ) local _GrSM = MENU_GROUP_COMMAND:New( group, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green )
missionCommands.addCommandForGroup( _gid, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange ) local _OrSM = MENU_GROUP_COMMAND:New( group, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange )
missionCommands.addCommandForGroup( _gid, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red ) local _ReSM = MENU_GROUP_COMMAND:New( group, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red )
missionCommands.addCommandForGroup( _gid, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White ) local _WhSm = MENU_GROUP_COMMAND:New( group, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White )
-- F10/On the Range/<Range Name>/My Settings/Flare Color/ -- F10/On the Range/<Range Name>/My Settings/Flare Color/
missionCommands.addCommandForGroup( _gid, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green ) local _GrFl = MENU_GROUP_COMMAND:New( group, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green )
missionCommands.addCommandForGroup( _gid, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red ) local _ReFl = MENU_GROUP_COMMAND:New( group, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red )
missionCommands.addCommandForGroup( _gid, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White ) local _WhFl = MENU_GROUP_COMMAND:New( group, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White )
missionCommands.addCommandForGroup( _gid, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow ) local _YeFl = MENU_GROUP_COMMAND:New( group, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow )
-- F10/On the Range/<Range Name>/My Settings/ -- F10/On the Range/<Range Name>/My Settings/
missionCommands.addCommandForGroup( _gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName ) local _SmDe = MENU_GROUP_COMMAND:New( group, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName ) local _SmIm = MENU_GROUP_COMMAND:New( group, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName ) local _FlHi = MENU_GROUP_COMMAND:New( group, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName )
missionCommands.addCommandForGroup( _gid, "All Messages On/Off", _settingsPath, self._MessagesToPlayerOnOff, self, _unitName ) local _AlMeA = MENU_GROUP_COMMAND:New( group, "All Messages On/Off", _settingsPath, self._MessagesToPlayerOnOff, self, _unitName )
local _TrpSh = MENU_GROUP_COMMAND:New( group, "Targetsheet On/Off", _settingsPath, self._TargetsheetOnOff, self, _unitName )
-- F10/On the Range/<Range Name>/Range Information -- F10/On the Range/<Range Name>/Range Information
missionCommands.addCommandForGroup( _gid, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName ) local _WeIn = MENU_GROUP_COMMAND:New( group, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName ) local _WeRe = MENU_GROUP_COMMAND:New( group, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName ) local _BoTgtgs = MENU_GROUP_COMMAND:New( group, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ) local _StrPits = MENU_GROUP_COMMAND:New( group, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ):Refresh()
end end
else else
self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName ) self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName )
@@ -3031,7 +3226,7 @@ end
-- Helper Functions -- Helper Functions
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get the coordinate of a Bomb target. --- Get the number of shells a unit currently has.
-- @param #RANGE self -- @param #RANGE self
-- @param #RANGE.BombTarget target Bomb target data. -- @param #RANGE.BombTarget target Bomb target data.
-- @return Core.Point#COORDINATE Target coordinate. -- @return Core.Point#COORDINATE Target coordinate.
@@ -3341,6 +3536,49 @@ function RANGE:_MessagesToPlayerOnOff( unitname )
end end
--- Targetsheet saves if player on or off.
-- @param #RANGE self
-- @param #string _unitname Name of the player unit.
function RANGE:_TargetsheetOnOff( _unitname )
self:F2( _unitname )
-- Get player unit and player name.
local unit, playername = self:_GetPlayerUnitAndName( _unitname )
-- Check if we have a player.
if unit and playername then
-- Player data.
local playerData = self.PlayerSettings[playername] -- #RANGE.PlayerData
if playerData then
-- Check if option is enabled at all.
local text = ""
if self.targetsheet then
-- Invert current setting.
playerData.targeton = not playerData.targeton
-- Inform player.
if playerData and playerData.targeton == true then
text = string.format( "roger, your targetsheets are now SAVED." )
else
text = string.format( "affirm, your targetsheets are NOT SAVED." )
end
else
text = "negative, target sheet data recorder is broken on this range."
end
-- Message to player.
-- self:MessageToPlayer(playerData, text, nil, playerData.name, 5)
self:_DisplayMessageToGroup( unit, text, 5, false, false )
end
end
end
--- Toggle status of flaring direct hits of range targets. --- Toggle status of flaring direct hits of range targets.
-- @param #RANGE self -- @param #RANGE self
-- @param #string unitname Name of the player unit. -- @param #string unitname Name of the player unit.
@@ -3507,7 +3745,7 @@ end
--- Checks if a static object with a certain name exists. It also added it to the MOOSE data base, if it is not already in there. --- Checks if a static object with a certain name exists. It also added it to the MOOSE data base, if it is not already in there.
-- @param #RANGE self -- @param #RANGE self
-- @param #string name Name of the potential static object. -- @param #string name Name of the potential static object.
-- @return #boolean Returns true if a static with this name exists. Returns false if a unit with this name exists. Returns nil if neither unit or static exist. -- @return #boolean Returns true if a static with this name exists. Retruns false if a unit with this name exists. Returns nil if neither unit or static exist.
function RANGE:_CheckStatic( name ) function RANGE:_CheckStatic( name )
self:F2( name ) self:F2( name )
@@ -3601,11 +3839,9 @@ function RANGE:_myname( unitname )
local unit = UNIT:FindByName( unitname ) local unit = UNIT:FindByName( unitname )
local pname = unit:GetPlayerName() local pname = unit:GetPlayerName()
-- local csign = unit:GetCallsign()
-- TODO: Either remove these leftovers, or implement them.
-- local csign=unit:GetCallsign()
-- return string.format("%s (%s)", csign, pname) -- return string.format("%s (%s)", csign, pname)
return string.format( "%s", pname ) return string.format( "%s", pname )
end end

View File

@@ -1650,7 +1650,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
PlayerName, PlayerName,
@@ -1705,7 +1705,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
PlayerName, PlayerName,

View File

@@ -19,7 +19,7 @@
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: Nov 2021 -- Last Update: Feb 2022
-- --
-- === -- ===
-- --
@@ -59,6 +59,7 @@ SEAD = {
Padding = 10, Padding = 10,
CallBack = nil, CallBack = nil,
UseCallBack = false, UseCallBack = false,
debug = false,
} }
--- Missile enumerators --- Missile enumerators
@@ -76,6 +77,8 @@ SEAD = {
["X_25"] = "X_25", ["X_25"] = "X_25",
["X_31"] = "X_31", ["X_31"] = "X_31",
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154",
} }
--- Missile enumerators - from DCS ME and Wikipedia --- Missile enumerators - from DCS ME and Wikipedia
@@ -85,7 +88,7 @@ SEAD = {
["AGM_88"] = { 150, 3}, ["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2}, ["AGM_45"] = { 12, 2},
["AGM_122"] = { 16.5, 2.3}, ["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.85}, ["AGM_84"] = { 280, 0.8},
["ALARM"] = { 45, 2}, ["ALARM"] = { 45, 2},
["LD-10"] = { 60, 4}, ["LD-10"] = { 60, 4},
["X_58"] = { 70, 4}, ["X_58"] = { 70, 4},
@@ -93,6 +96,8 @@ SEAD = {
["X_25"] = { 25, 0.76}, ["X_25"] = { 25, 0.76},
["X_31"] = {150, 3}, ["X_31"] = {150, 3},
["Kh25"] = {25, 0.8}, ["Kh25"] = {25, 0.8},
["BGM_109"] = {460, 0.705}, --in-game ~465kn
["AGM_154"] = {130, 0.61},
} }
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
@@ -108,8 +113,8 @@ SEAD = {
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
function SEAD:New( SEADGroupPrefixes, Padding ) function SEAD:New( SEADGroupPrefixes, Padding )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, FSM:New() )
self:F( SEADGroupPrefixes ) self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
@@ -122,14 +127,21 @@ function SEAD:New( SEADGroupPrefixes, Padding )
local padding = Padding or 10 local padding = Padding or 10
if padding < 10 then padding = 10 end if padding < 10 then padding = 10 end
self.Padding = padding self.Padding = padding
self.UseEmissionsOnOff = false self.UseEmissionsOnOff = true
self.debug = false
self.CallBack = nil self.CallBack = nil
self.UseCallBack = false self.UseCallBack = false
self:HandleEvent( EVENTS.Shot, self.HandleEventShot ) self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.3.3") -- Start State.
self:SetStartState("Running")
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.3")
return self return self
end end
@@ -213,7 +225,7 @@ function SEAD:_CheckHarms(WeaponName)
local hit = false local hit = false
local name = "" local name = ""
for _,_name in pairs (SEAD.Harms) do for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then if string.find(WeaponName,_name,1,true) then
hit = true hit = true
name = _name name = _name
break break
@@ -249,6 +261,186 @@ function SEAD:_GetDistance(_point1, _point2)
end end
end end
--- (Internal) Calculate hit zone of an AGM-88
-- @param #SEAD self
-- @param #table SEADWeapon DCS.Weapon object
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
-- @param #number height Height when the missile was fired
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
-- @param #string SEADWeaponName Weapon Name
-- @return #SEAD self
function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
if SEADWeapon and SEADWeapon:isExist() then
--local pos = SEADWeapon:getPoint()
-- postion and height
local position = SEADWeapon:getPosition()
local mheight = height
-- heading
local wph = math.atan2(position.x.z, position.x.x)
if wph < 0 then
wph=wph+2*math.pi
end
wph=math.deg(wph)
-- velocity
local wpndata = SEAD.HarmData["AGM_88"]
if string.find(SEADWeaponName,"154",1) then
wpndata = SEAD.HarmData["AGM_154"]
end
local mveloc = math.floor(wpndata[2] * 340.29)
local c1 = (2*mheight*9.81)/(mveloc^2)
local c2 = (mveloc^2) / 9.81
local Ropt = c2 * math.sqrt(c1+1)
if height <= 5000 then
Ropt = Ropt * 0.72
elseif height <= 7500 then
Ropt = Ropt * 0.82
elseif height <= 10000 then
Ropt = Ropt * 0.87
elseif height <= 12500 then
Ropt = Ropt * 0.98
end
-- look at a couple of zones across the trajectory
for n=1,3 do
local dist = Ropt - ((n-1)*20000)
local predpos= pos0:Translate(dist,wph)
if predpos then
local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
if self.debug then
predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
end
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce()
local tgtcoord = targetzone:GetRandomPointVec2()
--if tgtcoord and tgtcoord.ClassName == "COORDINATE" then
--local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
local tgtgrp = seadset:GetRandom()
local _targetgroup = nil
local _targetgroupname = "none"
local _targetskill = "Random"
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20)
end
--end
end
end
end
return self
end
--- (Internal) Handle Evasion
-- @param #SEAD self
-- @param #string _targetskill
-- @param Wrapper.Group#GROUP _targetgroup
-- @param Core.Point#COORDINATE SEADPlanePos
-- @param #string SEADWeaponName
-- @param Wrapper.Group#GROUP SEADGroup Attacker Group
-- @param #number timeoffset Offset for tti calc
-- @return #SEAD self
function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset)
local timeoffset = timeoffset or 0
if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ]
end
--self:T( _targetskill )
if self.TargetSkill[_targetskill] then
local _evade = math.random (1,100) -- random number for chance of evading action
if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T("*** SEAD - Evading")
-- calculate distance of attacker
local _targetpos = _targetgroup:GetCoordinate()
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
-- weapon speed
local hit, data = self:_CheckHarms(SEADWeaponName)
local wpnspeed = 666 -- ;)
local reach = 10
if hit then
local wpndata = SEAD.HarmData[data]
reach = wpndata[1] * 1,1
local mach = wpndata[2]
wpnspeed = math.floor(mach * 340.29)
end
-- time to impact
local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time
if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km
else
_distance = 0
end
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
if reach >= _distance then
self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Name
local attacker = args[3] -- Wrapper.Group#GROUP
if self.UseEmissionsOnOff then
grp:EnableEmission(false)
end
grp:OptionAlarmStateGreen() -- needed else we cannot move around
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name,attacker)
end
end
local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Nam
if self.UseEmissionsOnOff then
grp:EnableEmission(true)
end
grp:OptionAlarmStateRed()
grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[name] = false
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionEnd(grp,name)
end
end
-- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
if delay > _tti then delay = delay / 2 end -- speed up
if _tti > 600 then delay = _tti - 90 end -- shot from afar, 600 is default shorad ontime
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
local _targetgroupname = _targetgroup:GetName()
if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname, SEADGroup},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime, SEADGroup)
end
end
end
end
end
return self
end
--- (Internal) Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- (Internal) Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @param #SEAD self -- @param #SEAD self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -256,6 +448,7 @@ end
function SEAD:HandleEventShot( EventData ) function SEAD:HandleEventShot( EventData )
self:T( { EventData.id } ) self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName local SEADUnitName = EventData.IniDCSUnitName
@@ -270,114 +463,55 @@ function SEAD:HandleEventShot( EventData )
local _targetskill = "Random" local _targetskill = "Random"
local _targetgroupname = "none" local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target local _target = EventData.Weapon:getTarget() -- Identify target
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT if not _target or self.debug then -- AGM-88 or 154 w/o target data
self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
if string.find(SEADWeaponName,"AGM_88",1,true) or string.find(SEADWeaponName,"AGM_154",1,true) then
self:I("**** Tracking AGM-88/154 with no target data.")
local pos0 = SEADPlane:GetCoordinate()
local fheight = SEADPlane:GetHeight()
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
end
return self
end
local targetcat = _target:getCategory() -- Identify category
local _targetUnit = nil -- Wrapper.Unit#UNIT
local _targetgroup = nil -- Wrapper.Group#GROUP local _targetgroup = nil -- Wrapper.Group#GROUP
if _targetUnit and _targetUnit:IsAlive() then self:T(string.format("*** Targetcat = %d",targetcat))
_targetgroup = _targetUnit:GetGroup() if targetcat == Object.Category.UNIT then -- UNIT
_targetgroupname = _targetgroup:GetName() -- group name self:T("*** Target Category UNIT")
local _targetUnitName = _targetUnit:GetName() _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
_targetUnit:GetSkill() if _targetUnit and _targetUnit:IsAlive() then
_targetskill = _targetUnit:GetSkill() _targetgroup = _targetUnit:GetGroup()
_targetgroupname = _targetgroup:GetName() -- group name
local _targetUnitName = _targetUnit:GetName()
_targetUnit:GetSkill()
_targetskill = _targetUnit:GetSkill()
end
elseif targetcat == Object.Category.STATIC then
self:T("*** Target Category STATIC")
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterOnce()
local targetpoint = _target:getPoint() or {x=0,y=0,z=0}
local tgtcoord = COORDINATE:NewFromVec3(targetpoint)
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
end
end end
-- see if we are shot at -- see if we are shot at
local SEADGroupFound = false local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( _targetgroupname, SEADGroupPrefix ) self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
SEADGroupFound = true SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' ) self:T( '*** SEAD - Group Match Found' )
break break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked if SEADGroupFound == true then -- yes we are being attacked
if _targetskill == "Random" then -- when skill is random, choose a skill self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ]
end
--self:T( _targetskill )
if self.TargetSkill[_targetskill] then
local _evade = math.random (1,100) -- random number for chance of evading action
if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T("*** SEAD - Evading")
-- calculate distance of attacker
local _targetpos = _targetgroup:GetCoordinate()
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
-- weapon speed
local hit, data = self:_CheckHarms(SEADWeaponName)
local wpnspeed = 666 -- ;)
local reach = 10
if hit then
local wpndata = SEAD.HarmData[data]
reach = wpndata[1] * 1,1
local mach = wpndata[2]
wpnspeed = math.floor(mach * 340.29)
end
-- time to impact
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time
if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km
else
_distance = 0
end
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
if reach >= _distance then
self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Name
if self.UseEmissionsOnOff then
grp:EnableEmission(false)
end
grp:OptionAlarmStateGreen() -- needed else we cannot move around
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name)
end
end
local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Nam
if self.UseEmissionsOnOff then
grp:EnableEmission(true)
end
grp:OptionAlarmStateAuto()
grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[name] = false
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionEnd(grp,name)
end
end
-- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
if delay > _tti then delay = delay / 2 end -- speed up
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime)
end
end
end
end
end
end end
end end
return self return self

View File

@@ -4,6 +4,7 @@ __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/FiFo.lua' )
__Moose.Include( 'Scripts/Moose/Core/Base.lua' ) __Moose.Include( 'Scripts/Moose/Core/Base.lua' )
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' ) __Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )

View File

@@ -32,9 +32,11 @@
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module] -- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module]
-- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module] -- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
-- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59)) (CV-59) [Heatblur Carrier Module] -- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59)) (CV-59) [Heatblur Carrier Module]
-- * [HMS Hermes](https://en.wikipedia.org/wiki/HMS_Hermes_(R12)) (R12) [**WIP**]
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**] -- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**]
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**] -- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**]
-- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**] -- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**]
-- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_(L02)) (L02) [**WIP**]
-- --
-- **Supported Aircraft:** -- **Supported Aircraft:**
-- --
@@ -51,9 +53,9 @@
-- --
-- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier. -- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier.
-- --
-- The AV-8B Harrier, the USS Tarawa, USS America, HMAS Canberra and Juan Carlos I are WIP. The AV-8B harrier and the LHA's and LHD can only be used together, i.e. these ships are the only carriers the harrier is supposed to land on and -- The AV-8B Harrier, HMS Hermes, the USS Tarawa, USS America, HMAS Canberra, and Juan Carlos I are WIP. The AV-8B harrier and the LHA's and LHD can only be used together, i.e. these ships are the only carriers the harrier is supposed to land on and
-- no other fixed wing aircraft (human or AI controlled) are supposed to land on these ships. Currently only Case I is supported. Case II/III take slightly different steps from the CVN carrier. -- no other fixed wing aircraft (human or AI controlled) are supposed to land on these ships. Currently only Case I is supported. Case II/III take slightly different steps from the CVN carrier.
-- However, the two Case II/III pattern are very similar so this is not a big drawback. -- However, if no offset is used for the holding radial this provides a very close representation of the V/STOL Case III, allowing for an approach to over the deck and a vertical landing.
-- --
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version. -- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
-- --
@@ -113,10 +115,11 @@
-- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c) -- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c)
-- * [Updated Airboss V/STOL Features USS Tarawa](https://youtu.be/K7I4pU6j718) -- * [Updated Airboss V/STOL Features USS Tarawa](https://youtu.be/K7I4pU6j718)
-- * [Harrier Practice pattern USS America](https://youtu.be/99NigITYmcI) -- * [Harrier Practice pattern USS America](https://youtu.be/99NigITYmcI)
-- * [Harrier CASE III TACAN Approach USS Tarawa](https://www.youtube.com/watch?v=bTgJXZ9Mhdc&t=1s)
-- --
-- === -- ===
-- --
-- ### Author: **funkyfranky** -- ### Author: **funkyfranky** LHA and LHD V/STOL additions by **Pene**
-- ### Special Thanks To **Bankler** -- ### Special Thanks To **Bankler**
-- For his great [Recovery Trainer](https://forums.eagle.ru/showthread.php?t=221412) mission and script! -- For his great [Recovery Trainer](https://forums.eagle.ru/showthread.php?t=221412) mission and script!
-- His work was the initial inspiration for this class. Also note that this implementation uses some routines for determining the player position in Case I recoveries he developed. -- His work was the initial inspiration for this class. Also note that this implementation uses some routines for determining the player position in Case I recoveries he developed.
@@ -1260,7 +1263,7 @@ AIRBOSS = {
--- Aircraft types capable of landing on carrier (human+AI). --- Aircraft types capable of landing on carrier (human+AI).
-- @type AIRBOSS.AircraftCarrier -- @type AIRBOSS.AircraftCarrier
-- @field #string AV8B AV-8B Night Harrier. Works only with the USS Tarawa, USS America and Juan Carlos I. -- @field #string AV8B AV-8B Night Harrier. Works only with the HMS Hermes, USS Tarawa, USS America, and Juan Carlos I.
-- @field #string A4EC A-4E Community mod. -- @field #string A4EC A-4E Community mod.
-- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics. -- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics.
-- @field #string F14A F-14A by Heatblur. -- @field #string F14A F-14A by Heatblur.
@@ -1296,6 +1299,7 @@ AIRBOSS.AircraftCarrier={
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module] -- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
-- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module] -- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module]
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete] -- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
-- @field #string HERMES HMS Hermes (R12) [V/STOL Carrier]
-- @field #string TARAWA USS Tarawa (LHA-1) [V/STOL Carrier] -- @field #string TARAWA USS Tarawa (LHA-1) [V/STOL Carrier]
-- @field #string AMERICA USS America (LHA-6) [V/STOL Carrier] -- @field #string AMERICA USS America (LHA-6) [V/STOL Carrier]
-- @field #string JCARLOS Juan Carlos I (L61) [V/STOL Carrier] -- @field #string JCARLOS Juan Carlos I (L61) [V/STOL Carrier]
@@ -1309,6 +1313,7 @@ AIRBOSS.CarrierType = {
STENNIS = "Stennis", STENNIS = "Stennis",
FORRESTAL = "Forrestal", FORRESTAL = "Forrestal",
VINSON = "VINSON", VINSON = "VINSON",
HERMES = "HERMES81",
TARAWA = "LHA_Tarawa", TARAWA = "LHA_Tarawa",
AMERICA = "USS America LHA-6", AMERICA = "USS America LHA-6",
JCARLOS = "L61", JCARLOS = "L61",
@@ -1980,6 +1985,9 @@ function AIRBOSS:New( carriername, alias )
elseif self.carriertype == AIRBOSS.CarrierType.VINSON then elseif self.carriertype == AIRBOSS.CarrierType.VINSON then
-- TODO: Carl Vinson parameters. -- TODO: Carl Vinson parameters.
self:_InitStennis() self:_InitStennis()
elseif self.carriertype == AIRBOSS.CarrierType.HERMES then
-- Hermes parameters.
self:_InitHermes()
elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then
-- Tarawa parameters. -- Tarawa parameters.
self:_InitTarawa() self:_InitTarawa()
@@ -2082,7 +2090,7 @@ function AIRBOSS:New( carriername, alias )
-- cL:FlareYellow() -- cL:FlareYellow()
-- Carrier specific. -- Carrier specific.
if self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.JCARLOS or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.CANBERRA then if self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.HERMES or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.JCARLOS or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.CANBERRA then
-- Flare wires. -- Flare wires.
local w1 = stern:Translate( self.carrierparam.wire1, FB, true ) local w1 = stern:Translate( self.carrierparam.wire1, FB, true )
@@ -2811,13 +2819,29 @@ end
-- @param #number Low -- @param #number Low
-- @param #number LOW -- @param #number LOW
-- @return #AIRBOSS self -- @return #AIRBOSS self
function AIRBOSS:SetGlideslopeErrorThresholds( _max, _min, High, HIGH, Low, LOW )
self.gle._max = _max or 0.4 function AIRBOSS:SetGlideslopeErrorThresholds(_max,_min, High, HIGH, Low, LOW)
self.gle.High = High or 0.8
self.gle.HIGH = HIGH or 1.5 --Check if V/STOL Carrier
self.gle._min = _min or -0.3 if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
self.gle.Low = Low or -0.6
self.gle.LOW = LOW or -0.9 -- allow a larger GSE for V/STOL operations --Pene Testing
self.gle._max=_max or 0.7
self.gle.High=High or 1.4
self.gle.HIGH=HIGH or 1.9
self.gle._min=_min or -0.5
self.gle.Low=Low or -1.2
self.gle.LOW=LOW or -1.5
-- CVN values
else
self.gle._max=_max or 0.4
self.gle.High=High or 0.8
self.gle.HIGH=HIGH or 1.5
self.gle._min=_min or -0.3
self.gle.Low=Low or -0.6
self.gle.LOW=LOW or -0.9
end
return self return self
end end
@@ -2832,15 +2856,33 @@ end
-- @param #number RightMed -- @param #number RightMed
-- @param #number RIGHT -- @param #number RIGHT
-- @return #AIRBOSS self -- @return #AIRBOSS self
function AIRBOSS:SetLineupErrorThresholds( _max, _min, Left, LeftMed, LEFT, Right, RightMed, RIGHT )
self.lue._max = _max or 0.5 function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LeftMed, LEFT, Right, RightMed, RIGHT)
self.lue._min = _min or -0.5
self.lue.Left = Left or -1.0 --Check if V/STOL Carrier -- Pene testing
self.lue.LeftMed = LeftMed or -2.0 if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
self.lue.LEFT = LEFT or -3.0
self.lue.Right = Right or 1.0 -- V/STOL Values -- allow a larger LUE for V/STOL operations
self.lue.RightMed = RightMed or 2.0 self.lue._max=_max or 1.8
self.lue.RIGHT = RIGHT or 3.0 self.lue._min=_min or -1.8
self.lue.Left=Left or -2.8
self.lue.LeftMed=LeftMed or -3.8
self.lue.LEFT=LEFT or -4.5
self.lue.Right=Right or 2.8
self.lue.RightMed=RightMed or 3.8
self.lue.RIGHT=RIGHT or 4.5
-- CVN Values
else
self.lue._max=_max or 0.5
self.lue._min=_min or -0.5
self.lue.Left=Left or -1.0
self.lue.LeftMed=LeftMed or -2.0
self.lue.LEFT=LEFT or -3.0
self.lue.Right=Right or 1.0
self.lue.RightMed=RightMed or 2.0
self.lue.RIGHT=RIGHT or 3.0
end
return self return self
end end
@@ -4384,6 +4426,46 @@ function AIRBOSS:_InitForrestal()
end end
--- Init parameters for R12 HMS Hermes carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitHermes()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist = -105
self.carrierparam.deckheight = 12 -- From model viewer WL0.
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength = 228.19
self.carrierparam.totwidthport = 20.5
self.carrierparam.totwidthstarboard = 24.5
-- Landing runway.
self.carrierparam.rwyangle = 0
self.carrierparam.rwylength = 215
self.carrierparam.rwywidth = 13
-- Wires.
self.carrierparam.wire1 = nil
self.carrierparam.wire2 = nil
self.carrierparam.wire3 = nil
self.carrierparam.wire4 = nil
-- Late break.
self.BreakLate.name = "Late Break"
self.BreakLate.Xmin = -UTILS.NMToMeters( 1 ) -- Not more than 1 NM behind the boat. Last check was at 0.
self.BreakLate.Xmax = UTILS.NMToMeters( 5 ) -- Not more than 5 NM in front of the boat. Enough for late breaks?
self.BreakLate.Zmin = -UTILS.NMToMeters( 0.25 ) -- Not more than 0.25 NM port.
self.BreakLate.Zmax = UTILS.NMToMeters( 0.5 ) -- Not more than 0.5 NM starboard.
self.BreakLate.LimitXmin = 0 -- Check and next step 0.8 NM port and in front of boat.
self.BreakLate.LimitXmax = nil
self.BreakLate.LimitZmin = -UTILS.NMToMeters( 0.5 ) -- 926 m port, closer than the stennis as abeam is 0.8-1.0 rather than 1.2
self.BreakLate.LimitZmax = nil
end
--- Init parameters for LHA-1 Tarawa carrier. --- Init parameters for LHA-1 Tarawa carrier.
-- @param #AIRBOSS self -- @param #AIRBOSS self
function AIRBOSS:_InitTarawa() function AIRBOSS:_InitTarawa()
@@ -5007,14 +5089,16 @@ function AIRBOSS:_GetAircraftAoA( playerData )
aoa.Fast = 8.25 -- =17.5/2 aoa.Fast = 8.25 -- =17.5/2
aoa.FAST = 8.00 -- =16.5/2 aoa.FAST = 8.00 -- =16.5/2
elseif harrier then elseif harrier then
-- AV-8B Harrier parameters. Tuning done on the Fast AoA to allow for abeam and ninety at Nozzles 60 - 73.
aoa.SLOW = 14.0 -- AV-8B Harrier parameters. Tuning done on the Fast AoA to allow for abeam and ninety at Nozzles 55. Pene testing
aoa.Slow = 13.0 aoa.SLOW = 16.0
aoa.OnSpeedMax = 12.0 aoa.Slow = 13.5
aoa.OnSpeed = 11.0 aoa.OnSpeedMax = 12.5
aoa.OnSpeedMin = 10.0 aoa.OnSpeed = 10.0
aoa.Fast = 8.0 aoa.OnSpeedMin = 9.5
aoa.FAST = 7.5 aoa.Fast = 8.0
aoa.FAST = 7.5
end end
return aoa return aoa
@@ -5273,8 +5357,8 @@ function AIRBOSS:_GetAircraftParameters( playerData, step )
elseif skyhawk then elseif skyhawk then
alt = UTILS.FeetToMeters( 300 ) -- ? alt = UTILS.FeetToMeters( 300 ) -- ?
elseif harrier then elseif harrier then
-- 300-325 ft alt=UTILS.FeetToMeters(312)-- 300-325 ft
alt = UTILS.FeetToMeters( 300 ) -- Need to verify
end end
aoa = aoaac.OnSpeed aoa = aoaac.OnSpeed
@@ -6209,7 +6293,7 @@ function AIRBOSS:_GetMarshalAltitude( stack, case )
p2 = Carrier:Translate( UTILS.NMToMeters( 1.5 ), hdg ) p2 = Carrier:Translate( UTILS.NMToMeters( 1.5 ), hdg )
-- Tarawa,LHA,LHD Delta patterns. -- Tarawa,LHA,LHD Delta patterns.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Pattern is directly overhead the carrier. -- Pattern is directly overhead the carrier.
p1 = Carrier:Translate( UTILS.NMToMeters( 1.0 ), hdg + 90 ) p1 = Carrier:Translate( UTILS.NMToMeters( 1.0 ), hdg + 90 )
@@ -8048,7 +8132,7 @@ function AIRBOSS:OnEventLand( EventData )
self:T( self.lid .. text ) self:T( self.lid .. text )
-- Check carrier type. -- Check carrier type.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Power "Idle". -- Power "Idle".
self:RadioTransmission( self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true ) self:RadioTransmission( self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true )
@@ -8083,7 +8167,7 @@ function AIRBOSS:OnEventLand( EventData )
-- AI unit landed -- -- AI unit landed --
-------------------- --------------------
if self.carriertype ~= AIRBOSS.CarrierType.TARAWA or self.carriertype ~= AIRBOSS.CarrierType.AMERICA or self.carriertype ~= AIRBOSS.CarrierType.JCARLOS or self.carriertype ~= AIRBOSS.CarrierType.CANBERRA then if self.carriertype ~= AIRBOSS.CarrierType.HERMES or self.carriertype ~= AIRBOSS.CarrierType.TARAWA or self.carriertype ~= AIRBOSS.CarrierType.AMERICA or self.carriertype ~= AIRBOSS.CarrierType.JCARLOS or self.carriertype ~= AIRBOSS.CarrierType.CANBERRA then
-- Coordinate at landing event -- Coordinate at landing event
local coord = EventData.IniUnit:GetCoordinate() local coord = EventData.IniUnit:GetCoordinate()
@@ -9121,7 +9205,7 @@ function AIRBOSS:_CheckForLongDownwind( playerData )
local limit = UTILS.NMToMeters( -1.6 ) local limit = UTILS.NMToMeters( -1.6 )
-- For the tarawa, other LHA and LHD we give a bit more space. -- For the tarawa, other LHA and LHD we give a bit more space.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
limit = UTILS.NMToMeters( -2.0 ) limit = UTILS.NMToMeters( -2.0 )
end end
@@ -9208,7 +9292,7 @@ function AIRBOSS:_Ninety( playerData )
self:_PlayerHint( playerData ) self:_PlayerHint( playerData )
-- Next step: wake. -- Next step: wake.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Harrier has no wake stop. It stays port of the boat. -- Harrier has no wake stop. It stays port of the boat.
self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.FINAL ) self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.FINAL )
else else
@@ -9464,8 +9548,9 @@ function AIRBOSS:_Groove( playerData )
-- Speed difference. -- Speed difference.
local dv = math.abs( vplayer - vcarrier ) local dv = math.abs( vplayer - vcarrier )
-- Stable when speed difference < 20 km/h.
local stable = dv < 20 -- Stable when speed difference < 30 km/h.(16 Kts)Pene Testing
local stable=dv<30
-- Check if player is inside the zone. -- Check if player is inside the zone.
if playerData.unit:IsInZone( ZoneALS ) and stable then if playerData.unit:IsInZone( ZoneALS ) and stable then
@@ -9497,8 +9582,8 @@ function AIRBOSS:_Groove( playerData )
-- Speed difference. -- Speed difference.
local dv = math.abs( vplayer - vcarrier ) local dv = math.abs( vplayer - vcarrier )
-- Stable when v<10 km/h. -- Stable when v<15 km/h.
local stable = dv < 10 local stable=dv<15
-- Radio Transmission "Stabilized" once the aircraft has been cleared to cross and is over the Landing Spot and stable. -- Radio Transmission "Stabilized" once the aircraft has been cleared to cross and is over the Landing Spot and stable.
if playerData.unit:IsInZone( ZoneLS ) and stable and playerData.stable == true then if playerData.unit:IsInZone( ZoneLS ) and stable and playerData.stable == true then
@@ -9537,25 +9622,26 @@ function AIRBOSS:_Groove( playerData )
-- Nothing else necessary. -- Nothing else necessary.
return return
end end
end
-- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP) end
-- if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then -- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP)--- Need to think more about this.
-- Get groove time
-- local vSlow=groovedata.time --if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then
-- If too slow wave off. -- Get groove time
-- if vSlow >75 then --local vSlow=groovedata.time
-- If too slow wave off.
-- LSO Wave off! --if vSlow >75 then
-- self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true)
-- playerData.Tlso=timer.getTime() -- LSO Wave off!
--self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true)
-- Player was waved Off --playerData.Tlso=timer.getTime()
-- playerData.waveoff=true
-- return -- Player was waved Off
-- end --playerData.waveoff=true
-- end --return
--end
--end
-- Groovedata step. -- Groovedata step.
groovedata.Step = playerData.step groovedata.Step = playerData.step
@@ -9726,8 +9812,8 @@ function AIRBOSS:_CheckWaveOff( glideslopeError, lineupError, AoA, playerData )
-- For the harrier, we allow a bit more room. -- For the harrier, we allow a bit more room.
if playerData.actype == AIRBOSS.AircraftCarrier.AV8B then if playerData.actype == AIRBOSS.AircraftCarrier.AV8B then
glMax = 2.6 glMax = 2.6
glMin = -2.0 glMin = -2.2 -- Testing, @Engines may be just dragging it in on Hermes, or the carrier parameters need adjusting.
luAbs = 4.1 -- Testing Pene (WIP) needs feedback to tighten up tolerences. luAbs = 4.1 -- Testing Pene.
end end
@@ -9893,17 +9979,23 @@ function AIRBOSS:_GetSternCoord()
local hdg = self.carrier:GetHeading() local hdg = self.carrier:GetHeading()
-- Final bearing (true). -- Final bearing (true).
local FB = self:GetFinalBearing() local FB=self:GetFinalBearing()
local case=self.case
-- Stern coordinate (sterndist<0). Also translate 10 meters starboard wrt Final bearing. -- Stern coordinate (sterndist<0). Also translate 10 meters starboard wrt Final bearing.
self.sterncoord:UpdateFromCoordinate( self:GetCoordinate() ) self.sterncoord:UpdateFromCoordinate( self:GetCoordinate() )
-- local stern=self:GetCoordinate() -- local stern=self:GetCoordinate()
-- Stern coordinate (sterndist<0). -- Stern coordinate (sterndist<0). --Pene testing Case III
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-- Tarawa: Translate 8 meters port. if case==3 then
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 8, FB - 90, true, true ) -- CASE III V/STOL translation Due over deck approach if needed.
elseif self.carriertype == AIRBOSS.CarrierType.STENNIS then self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
elseif case==2 or case==1 then
-- V/Stol: Translate 8 meters port.
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
end
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
-- Stennis: translate 7 meters starboard wrt Final bearing. -- Stennis: translate 7 meters starboard wrt Final bearing.
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true ) self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true )
elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then
@@ -10536,7 +10628,8 @@ function AIRBOSS:_GetZoneRunwayBox()
return self.zoneRunwaybox return self.zoneRunwaybox
end end
--- Get zone of primary abeam landing position of USS Tarawa, USS America and Juan Carlos. Box length 50 meters and width 30 meters. --- Get zone of primary abeam landing position of HMS Hermes, USS Tarawa, USS America and Juan Carlos. Box length 50 meters and width 30 meters.
--- Allow for Clear to land call from LSO approaching abeam the landing spot if stable as per NATOPS 00-80T --- Allow for Clear to land call from LSO approaching abeam the landing spot if stable as per NATOPS 00-80T
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @return Core.Zone#ZONE_POLYGON Zone surrounding landing runway. -- @return Core.Zone#ZONE_POLYGON Zone surrounding landing runway.
@@ -10548,9 +10641,9 @@ function AIRBOSS:_GetZoneAbeamLandingSpot()
-- Current carrier heading. -- Current carrier heading.
local FB = self:GetFinalBearing( false ) local FB = self:GetFinalBearing( false )
-- Coordinate array. -- Coordinate array. Pene Testing extended Abeam landing spot V/STOL.
local p = {} local p={}
-- Points. -- Points.
p[1] = S:Translate( 15, FB ):Translate( 15, FB + 90 ) -- Top-Right p[1] = S:Translate( 15, FB ):Translate( 15, FB + 90 ) -- Top-Right
p[2] = S:Translate( -45, FB ):Translate( 15, FB + 90 ) -- Bottom-Right p[2] = S:Translate( -45, FB ):Translate( 15, FB + 90 ) -- Bottom-Right
@@ -10640,7 +10733,7 @@ function AIRBOSS:_GetZoneHolding( case, stack )
self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", Post:GetVec2(), self.marshalradius ) self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", Post:GetVec2(), self.marshalradius )
-- Delta pattern. -- Delta pattern.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters( 5 ) ) self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters( 5 ) )
end end
@@ -10692,7 +10785,7 @@ function AIRBOSS:_GetZoneCommence( case, stack )
-- Three position -- Three position
local Three = self:GetCoordinate():Translate( D, hdg + 275 ) local Three = self:GetCoordinate():Translate( D, hdg + 275 )
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
local Dx = UTILS.NMToMeters( 2.25 ) local Dx = UTILS.NMToMeters( 2.25 )
@@ -10983,7 +11076,7 @@ function AIRBOSS:_GetAltCarrier( unit )
return h return h
end end
--- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa and America we take the abeam landing spot 120 ft abeam the 7.5 position, for the Juan Carlos I it is 120 ft and abeam the 5 position. --- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa, Canberrra, Juan Carlos and America we take the abeam landing spot 120 ft above and 21 ft abeam the 7.5 position, for the Juan Carlos I and HMS Hermes it is 120 ft above and 21 ft abeam the 5 position. For CASE III it is 120ft directly above the landing spot.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @return Core.Point#COORDINATE Optimal landing coordinate. -- @return Core.Point#COORDINATE Optimal landing coordinate.
function AIRBOSS:_GetOptLandingCoordinate() function AIRBOSS:_GetOptLandingCoordinate()
@@ -10993,45 +11086,28 @@ function AIRBOSS:_GetOptLandingCoordinate()
-- Stern coordinate. -- Stern coordinate.
-- local stern=self:_GetSternCoord() -- local stern=self:_GetSternCoord()
-- Final bearing. -- Final bearing.
local FB = self:GetFinalBearing( false )
if self.carriertype == AIRBOSS.CarrierType.TARAWA then local FB=self:GetFinalBearing(false)
local case=self.case
-- set Case III V/STOL abeam landing spot over deck -- Pene Testing
if self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
if case==3 then
self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate())
-- Altitude 120ft -- is this corect for Case III?
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
elseif case==2 or case==1 then
-- Landing 100 ft abeam, 120 ft alt. -- Landing 100 ft abeam, 120 ft alt.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true ) self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-90, true, true)
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90) --stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft. -- Alitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) ) self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
elseif self.carriertype == AIRBOSS.CarrierType.AMERICA then end
-- Landing 100 ft abeam, 120 ft alt. To allow adjustments to match different deck configurations.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true )
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) )
elseif self.carriertype == AIRBOSS.CarrierType.JCARLOS then
-- Landing 100 ft abeam, 120 ft alt.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true )
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) )
elseif self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Landing 100 ft abeam, 120 ft alt.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true )
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) )
else else
-- Ideally we want to land between 2nd and 3rd wire. -- Ideally we want to land between 2nd and 3rd wire.
@@ -11059,7 +11135,14 @@ function AIRBOSS:_GetLandingSpotCoordinate()
-- Stern coordinate. -- Stern coordinate.
-- local stern=self:_GetSternCoord() -- local stern=self:_GetSternCoord()
if self.carriertype == AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.HERMES then
-- Landing 100 ft abeam, 100 alt.
local hdg = self:GetHeading()
-- Primary landing spot 5
self.landingspotcoord:Translate( 69, hdg, true, true ):SetAltitude( self.carrierparam.deckheight )
elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then
-- Landing 100 ft abeam, 120 alt. -- Landing 100 ft abeam, 120 alt.
local hdg = self:GetHeading() local hdg = self:GetHeading()
@@ -11649,15 +11732,17 @@ function AIRBOSS:_LSOgrade( playerData )
local G = GXX .. " " .. GIM .. " " .. " " .. GIC .. " " .. GAR local G = GXX .. " " .. GIM .. " " .. " " .. GIC .. " " .. GAR
-- Count number of minor, normal and major deviations. -- Count number of minor, normal and major deviations.
local N = nXX + nIM + nIC + nAR local N=nXX+nIM+nIC+nAR
local nL = count( G, '_' ) / 2 local Nv=nXX+nIM
local nS = count( G, '%(' ) local nL=count(G, '_')/2
local nN = N - nS - nL local nS=count(G, '%(')
local nN=N-nS-nL
-- Groove time 15-18.99 sec for a unicorn. Or 65-70 for V/STOL unicorn. local nNv=Nv-nS-nL
local Tgroove = playerData.Tgroove
local TgrooveUnicorn = Tgroove and (Tgroove >= 15.0 and Tgroove <= 18.99) or false -- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn.
local TgrooveVstolUnicorn = Tgroove and (Tgroove >= 60.0 and Tgroove <= 65.0) and playerData.actype == AIRBOSS.AircraftCarrier.AV8B or false local Tgroove=playerData.Tgroove
local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false
local TgrooveVstolUnicorn=Tgroove and (Tgroove>=60.0 and Tgroove<=65.0)and playerData.actype==AIRBOSS.AircraftCarrier.AV8B or false
local grade local grade
local points local points
@@ -11668,29 +11753,33 @@ function AIRBOSS:_LSOgrade( playerData )
G = "Unicorn" G = "Unicorn"
else else
-- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe. (WIP requires feedback) -- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe.--Pene testing
-- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation. -- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation.
if nL > 3 and playerData.actype == AIRBOSS.AircraftCarrier.AV8B then if nL > 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Larger deviations ==> "No grade" 2.0 points. -- Larger deviations ==> "No grade" 2.0 points.
grade = "--" grade="--"
points = 2.0 points=2.0
elseif nN > 2 and playerData.actype == AIRBOSS.AircraftCarrier.AV8B then elseif nNv >= 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections. -- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade = "(OK)" grade="(OK)"
points = 3.0 points=3.0
elseif nL > 0 then elseif nNv < 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only minor average deviations ==> "OK" Pass with minor deviations and corrections. (test nNv<=1 and)
grade="OK"
points=4.0
elseif nL > 0 then
-- Larger deviations ==> "No grade" 2.0 points. -- Larger deviations ==> "No grade" 2.0 points.
grade = "--" grade="--"
points = 2.0 points=2.0
elseif nN > 0 then elseif nN> 0 then
-- No larger but average deviations ==> "Fair Pass" Pass with average deviations and corrections. -- No larger but average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade = "(OK)" grade="(OK)"
points = 3.0 points=3.0
else else
-- Only minor corrections -- Only minor corrections
grade = "OK" grade="OK"
points = 4.0 points=4.0
end end
end end
@@ -11994,7 +12083,7 @@ function AIRBOSS:_GS( step, n )
if n == -1 then if n == -1 then
gp = AIRBOSS.GroovePos.IC gp = AIRBOSS.GroovePos.IC
elseif n == 1 then elseif n == 1 then
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
gp = AIRBOSS.GroovePos.AL gp = AIRBOSS.GroovePos.AL
else else
gp = AIRBOSS.GroovePos.IW gp = AIRBOSS.GroovePos.IW
@@ -13873,7 +13962,7 @@ function AIRBOSS:_IsCarrierAircraft( unit )
-- Special case for Harrier which can only land on Tarawa, LHA and LHD. -- Special case for Harrier which can only land on Tarawa, LHA and LHD.
if aircrafttype == AIRBOSS.AircraftCarrier.AV8B then if aircrafttype == AIRBOSS.AircraftCarrier.AV8B then
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
return true return true
else else
return false return false
@@ -13881,7 +13970,7 @@ function AIRBOSS:_IsCarrierAircraft( unit )
end end
-- Also only Harriers can land on the Tarawa, LHA and LHD. -- Also only Harriers can land on the Tarawa, LHA and LHD.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
if aircrafttype ~= AIRBOSS.AircraftCarrier.AV8B then if aircrafttype ~= AIRBOSS.AircraftCarrier.AV8B then
return false return false
end end
@@ -17238,7 +17327,7 @@ function AIRBOSS:_MarkCaseZones( _unitName, flare )
end end
-- Tarawa, LHA and LHD landing spots. -- Tarawa, LHA and LHD landing spots.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
text = text .. "\n* abeam landing stop with RED flares" text = text .. "\n* abeam landing stop with RED flares"
-- Abeam landing spot zone. -- Abeam landing spot zone.
local ALSPT = self:_GetZoneAbeamLandingSpot() local ALSPT = self:_GetZoneAbeamLandingSpot()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,7 @@
-- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send.
-- @field #string path Path to the SRS exe. This includes the final slash "/". -- @field #string path Path to the SRS exe. This includes the final slash "/".
-- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
-- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
@@ -106,6 +107,14 @@
-- --
-- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted. -- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted.
-- --
-- ## Set SRS Port
--
-- Use @{#MSRS.SetPort} to define the SRS port. Defaults to 5002.
--
-- ## Set SRS Volume
--
-- Use @{#MSRS.SetVolume} to define the SRS volume. Defaults to 1.0. Allowed values are between 0.0 and 1.0, from silent to loudest.
--
-- @field #MSRS -- @field #MSRS
MSRS = { MSRS = {
ClassName = "MSRS", ClassName = "MSRS",
@@ -121,11 +130,12 @@ MSRS = {
volume = 1, volume = 1,
speed = 1, speed = 1,
coordinate = nil, coordinate = nil,
Label = "ROBOT",
} }
--- MSRS class version. --- MSRS class version.
-- @field #string version -- @field #string version
MSRS.version="0.0.3" MSRS.version="0.0.5"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -144,8 +154,9 @@ MSRS.version="0.0.3"
-- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #string PathToSRS Path to the directory, where SRS is located.
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self -- @return #MSRS self
function MSRS:New(PathToSRS, Frequency, Modulation) function MSRS:New(PathToSRS, Frequency, Modulation, Volume)
-- Defaults. -- Defaults.
Frequency =Frequency or 143 Frequency =Frequency or 143
@@ -160,6 +171,13 @@ function MSRS:New(PathToSRS, Frequency, Modulation)
self:SetModulations(Modulation) self:SetModulations(Modulation)
self:SetGender() self:SetGender()
self:SetCoalition() self:SetCoalition()
self:SetLabel()
self:SetVolume()
self.lid = string.format("%s-%s | ",self.name,self.version)
if not io or not os then
self:E(self.lid.."***** ERROR - io or os NOT desanitized! MSRS will not work!")
end
return self return self
end end
@@ -202,12 +220,47 @@ function MSRS:GetPath()
return self.path return self.path
end end
--- Set SRS volume.
-- @param #MSRS self
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self
function MSRS:SetVolume(Volume)
local volume = Volume or 1
if volume > 1 then volume = 1 elseif volume < 0 then volume = 0 end
self.volume = volume
return self
end
--- Get SRS volume.
-- @param #MSRS self
-- @return #number Volume Volume - 1.0 is max, 0.0 is silence
function MSRS:GetVolume()
return self.volume
end
--- Set label.
-- @param #MSRS self
-- @param #number Label. Default "ROBOT"
-- @return #MSRS self
function MSRS:SetLabel(Label)
self.Label=Label or "ROBOT"
return self
end
--- Get label.
-- @param #MSRS self
-- @return #number Label.
function MSRS:GetLabel()
return self.Label
end
--- Set port. --- Set port.
-- @param #MSRS self -- @param #MSRS self
-- @param #number Port Port. Default 5002. -- @param #number Port Port. Default 5002.
-- @return #MSRS self -- @return #MSRS self
function MSRS:SetPort(Port) function MSRS:SetPort(Port)
self.port=Port or 5002 self.port=Port or 5002
return self
end end
--- Get port. --- Get port.
@@ -223,6 +276,7 @@ end
-- @return #MSRS self -- @return #MSRS self
function MSRS:SetCoalition(Coalition) function MSRS:SetCoalition(Coalition)
self.coalition=Coalition or 0 self.coalition=Coalition or 0
return self
end end
--- Get coalition. --- Get coalition.
@@ -391,7 +445,7 @@ function MSRS:PlaySoundFile(Soundfile, Delay)
local command=self:_GetCommand() local command=self:_GetCommand()
-- Append file. -- Append file.
command=command.." --file="..tostring(soundfile) command=command..' --file="'..tostring(soundfile)..'"'
self:_ExecCommand(command) self:_ExecCommand(command)
@@ -634,8 +688,9 @@ end
-- @param #number volume Volume. -- @param #number volume Volume.
-- @param #number speed Speed. -- @param #number speed Speed.
-- @param #number port Port. -- @param #number port Port.
-- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed!
-- @return #string Command. -- @return #string Command.
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port) function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label)
local path=self:GetPath() or STTS.DIRECTORY local path=self:GetPath() or STTS.DIRECTORY
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
@@ -648,6 +703,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
volume=volume or self.volume volume=volume or self.volume
speed=speed or self.speed speed=speed or self.speed
port=port or self.port port=port or self.port
label=label or self.Label
-- Replace modulation -- Replace modulation
modus=modus:gsub("0", "AM") modus=modus:gsub("0", "AM")
@@ -657,12 +713,12 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed) --local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
-- Command from orig STTS script. Works better for some unknown reason! -- Command from orig STTS script. Works better for some unknown reason!
local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT") --local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT") --local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
-- Command. -- Command.
local command=string.format('%s/%s -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, "ROBOT") local command=string.format('"%s\\%s" -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, label)
-- Set voice or gender/culture. -- Set voice or gender/culture.
if voice then if voice then
@@ -671,7 +727,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
else else
-- Add gender. -- Add gender.
if gender and gender~="female" then if gender and gender~="female" then
command=command..string.format(" --gender=%s", tostring(gender)) command=command..string.format(" -g %s", tostring(gender))
end end
-- Add culture. -- Add culture.
if culture and culture~="en-GB" then if culture and culture~="en-GB" then

View File

@@ -183,13 +183,17 @@ do -- Sound File
--- Set path, where the sound file is located. --- Set path, where the sound file is located.
-- @param #SOUNDFILE self -- @param #SOUNDFILE self
-- @param #string Path Path to the directory, where the sound file is located. -- @param #string Path Path to the directory, where the sound file is located. In case this is nil, it defaults to the DCS mission temp directory.
-- @return #SOUNDFILE self -- @return #SOUNDFILE self
function SOUNDFILE:SetPath(Path) function SOUNDFILE:SetPath(Path)
-- Init path. -- Init path.
self.path=Path or "l10n/DEFAULT/" self.path=Path or "l10n/DEFAULT/"
if not Path and self.useSRS then -- use path to mission temp dir
self.path = os.getenv('TMP') .. "\\DCS\\Mission\\l10n\\DEFAULT"
end
-- Remove (back)slashes. -- Remove (back)slashes.
local nmax=1000 ; local n=1 local nmax=1000 ; local n=1
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do

View File

@@ -40,7 +40,7 @@ do -- UserSound
-- @param #USERSOUND self -- @param #USERSOUND self
-- @param #string UserSoundFileName The filename of the usersound. -- @param #string UserSoundFileName The filename of the usersound.
-- @return #USERSOUND -- @return #USERSOUND
function USERSOUND:New( UserSoundFileName ) --R2.3 function USERSOUND:New( UserSoundFileName )
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
@@ -58,7 +58,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound. -- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
-- --
function USERSOUND:SetFileName( UserSoundFileName ) --R2.3 function USERSOUND:SetFileName( UserSoundFileName )
self.UserSoundFileName = UserSoundFileName self.UserSoundFileName = UserSoundFileName
@@ -75,7 +75,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToAll() -- Play the sound that Blue has won. -- BlueVictory:ToAll() -- Play the sound that Blue has won.
-- --
function USERSOUND:ToAll() --R2.3 function USERSOUND:ToAll()
trigger.action.outSound( self.UserSoundFileName ) trigger.action.outSound( self.UserSoundFileName )
@@ -91,7 +91,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition. -- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
-- --
function USERSOUND:ToCoalition( Coalition ) --R2.3 function USERSOUND:ToCoalition( Coalition )
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName ) trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
@@ -107,7 +107,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country. -- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
-- --
function USERSOUND:ToCountry( Country ) --R2.3 function USERSOUND:ToCountry( Country )
trigger.action.outSoundForCountry( Country, self.UserSoundFileName ) trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
@@ -123,9 +123,9 @@ do -- UserSound
-- @usage -- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player. -- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group. -- BlueVictory:ToGroup( PlayerGroup ) -- Play the victory sound to the player group.
-- --
function USERSOUND:ToGroup( Group, Delay ) --R2.3 function USERSOUND:ToGroup( Group, Delay )
Delay=Delay or 0 Delay=Delay or 0
if Delay>0 then if Delay>0 then
@@ -136,5 +136,27 @@ do -- UserSound
return self return self
end end
--- Play the usersound to the given @{Wrapper.Unit}.
-- @param #USERSOUND self
-- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to.
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerUnit = UNIT:FindByName( "PlayerUnit" ) -- Search for the active group named "PlayerUnit", a human player.
-- BlueVictory:ToUnit( PlayerUnit ) -- Play the victory sound to the player unit.
--
function USERSOUND:ToUnit( Unit, Delay )
Delay=Delay or 0
if Delay>0 then
SCHEDULER:New(nil, USERSOUND.ToUnit,{self, Unit}, Delay)
else
trigger.action.outSoundForUnit( Unit:GetID(), self.UserSoundFileName )
end
return self
end
end end

View File

@@ -1,18 +1,18 @@
--- **Utilities** Enumerators. --- **Utilities** Enumerators.
-- --
-- An enumerator is a variable that holds a constant value. Enumerators are very useful because they make the code easier to read and to change in general. -- An enumerator is a variable that holds a constant value. Enumerators are very useful because they make the code easier to read and to change in general.
-- --
-- For example, instead of using the same value at multiple different places in your code, you should use a variable set to that value. -- For example, instead of using the same value at multiple different places in your code, you should use a variable set to that value.
-- If, for whatever reason, the value needs to be changed, you only have to change the variable once and do not have to search through you code and reset -- If, for whatever reason, the value needs to be changed, you only have to change the variable once and do not have to search through you code and reset
-- every value by hand. -- every value by hand.
-- --
-- Another big advantage is that the LDT intellisense "knows" the enumerators. So you can use the autocompletion feature and do not have to keep all the -- Another big advantage is that the LDT intellisense "knows" the enumerators. So you can use the autocompletion feature and do not have to keep all the
-- values in your head or look them up in the docs. -- values in your head or look them up in the docs.
-- --
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit. -- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
-- --
-- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names. -- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
-- --
-- @module ENUMS -- @module ENUMS
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -20,7 +20,7 @@
-- @type ENUMS -- @type ENUMS
--- Because ENUMS are just better practice. --- Because ENUMS are just better practice.
-- --
-- The ENUMS class adds some handy variables, which help you to make your code better and more general. -- The ENUMS class adds some handy variables, which help you to make your code better and more general.
-- --
-- @field #ENUMS -- @field #ENUMS
@@ -30,16 +30,16 @@ ENUMS = {}
-- @type ENUMS.ROE -- @type ENUMS.ROE
-- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. -- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
-- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking. -- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking.
-- @field #number OpenFire AI will engage only targets specified in its tasking. -- @field #number OpenFire AI will engage only targets specified in its taskings.
-- @field #number ReturnFire AI will only engage threats that shoot first. -- @field #number ReturnFire AI will only engage threats that shoot first.
-- @field #number WeaponHold AI will hold fire under all circumstances. -- @field #number WeaponHold AI will hold fire under all circumstances.
ENUMS.ROE = { ENUMS.ROE = {
WeaponFree = 0, WeaponFree=0,
OpenFireWeaponFree = 1, OpenFireWeaponFree=1,
OpenFire = 2, OpenFire=2,
ReturnFire = 3, ReturnFire=3,
WeaponHold = 4, WeaponHold=4,
} }
--- Reaction On Threat. --- Reaction On Threat.
-- @type ENUMS.ROT -- @type ENUMS.ROT
@@ -49,11 +49,11 @@ ENUMS.ROE = {
-- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats. -- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats.
-- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base. -- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
ENUMS.ROT = { ENUMS.ROT = {
NoReaction = 0, NoReaction=0,
PassiveDefense = 1, PassiveDefense=1,
EvadeFire = 2, EvadeFire=2,
BypassAndEscape = 3, BypassAndEscape=3,
AllowAbortMission = 4, AllowAbortMission=4,
} }
--- Alarm state. --- Alarm state.
@@ -62,12 +62,12 @@ ENUMS.ROT = {
-- @field #number Green Group is not combat ready. Sensors are stowed if possible. -- @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. -- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
ENUMS.AlarmState = { ENUMS.AlarmState = {
Auto = 0, Auto=0,
Green = 1, Green=1,
Red = 2, Red=2,
} }
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on Hoggit wiki. --- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
-- @type ENUMS.WeaponFlag -- @type ENUMS.WeaponFlag
ENUMS.WeaponFlag={ ENUMS.WeaponFlag={
-- Bombs -- Bombs
@@ -111,7 +111,7 @@ ENUMS.WeaponFlag={
-- --
-- Bombs -- Bombs
GuidedBomb = 14, -- (LGB + TvGB + SNSGB) GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispenser + CandleBomb + ParachuteBomb) AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb) AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
--- Rockets --- Rockets
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
@@ -123,9 +123,11 @@ ENUMS.WeaponFlag={
--- Air-To-Air Missiles --- Air-To-Air Missiles
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
AnyMissile = 268402688, -- AnyASM + AnyAAM AnyMissile = 268402688, -- AnyASM + AnyAAM
--- Guns --- Guns
Cannons = 805306368, -- GUN_POD + BuiltInCannon Cannons = 805306368, -- GUN_POD + BuiltInCannon
--- Torpedo
Torpedo = 4294967296,
--- ---
-- Even More Genral -- Even More Genral
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons) Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
@@ -133,9 +135,96 @@ ENUMS.WeaponFlag={
AnyAG = 2956984318, -- Any Air-To-Ground Weapon AnyAG = 2956984318, -- Any Air-To-Ground Weapon
AnyAA = 264241152, -- Any Air-To-Air Weapon AnyAA = 264241152, -- Any Air-To-Air Weapon
AnyUnguided = 2952822768, -- Any Unguided Weapon AnyUnguided = 2952822768, -- Any Unguided Weapon
AnyGuided = 268402702, -- Any Guided Weapon AnyGuided = 268402702, -- Any Guided Weapon
} }
--- Weapon types by category. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on hoggit wiki.
-- @type ENUMS.WeaponType
-- @field #table Bomb Bombs.
-- @field #table Rocket Rocket.
-- @field #table Gun Guns.
-- @field #table Missile Missiles.
-- @field #table AAM Air-to-Air missiles.
-- @field #table Torpedo Torpedos.
-- @field #table Any Combinations.
ENUMS.WeaponType={}
ENUMS.WeaponType.Bomb={
-- Bombs
LGB = 2,
TvGB = 4,
SNSGB = 8,
HEBomb = 16,
Penetrator = 32,
NapalmBomb = 64,
FAEBomb = 128,
ClusterBomb = 256,
Dispencer = 512,
CandleBomb = 1024,
ParachuteBomb = 2147483648,
-- Combinations
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
}
ENUMS.WeaponType.Rocket={
-- Rockets
LightRocket = 2048,
MarkerRocket = 4096,
CandleRocket = 8192,
HeavyRocket = 16384,
-- Combinations
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
}
ENUMS.WeaponType.Gun={
-- Guns
GunPod = 268435456,
BuiltInCannon = 536870912,
-- Combinations
Cannons = 805306368, -- GUN_POD + BuiltInCannon
}
ENUMS.WeaponType.Missile={
-- Missiles
AntiRadarMissile = 32768,
AntiShipMissile = 65536,
AntiTankMissile = 131072,
FireAndForgetASM = 262144,
LaserASM = 524288,
TeleASM = 1048576,
CruiseMissile = 2097152,
AntiRadarMissile2 = 1073741824,
-- Combinations
GuidedASM = 1572864, -- (LaserASM + TeleASM)
TacticalASM = 1835008, -- (GuidedASM + FireAndForgetASM)
AnyASM = 4161536, -- (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile)
AnyASM2 = 1077903360, -- 4161536+1073741824,
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
AnyMissile = 268402688, -- AnyASM + AnyAAM
}
ENUMS.WeaponType.AAM={
-- Air-To-Air Missiles
SRAM = 4194304,
MRAAM = 8388608,
LRAAM = 16777216,
IR_AAM = 33554432,
SAR_AAM = 67108864,
AR_AAM = 134217728,
-- Combinations
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
}
ENUMS.WeaponType.Torpedo={
-- Torpedo
Torpedo = 4294967296,
}
ENUMS.WeaponType.Any={
-- General combinations
Weapon = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
AG = 2956984318, -- Any Air-To-Ground Weapon
AA = 264241152, -- Any Air-To-Air Weapon
Unguided = 2952822768, -- Any Unguided Weapon
Guided = 268402702, -- Any Guided Weapon
}
--- Mission tasks. --- Mission tasks.
-- @type ENUMS.MissionTask -- @type ENUMS.MissionTask
-- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics. -- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.
@@ -173,7 +262,7 @@ ENUMS.MissionTask={
TRANSPORT="Transport", TRANSPORT="Transport",
} }
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki. --- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
-- @type ENUMS.Formation -- @type ENUMS.Formation
ENUMS.Formation={} ENUMS.Formation={}
ENUMS.Formation.FixedWing={} ENUMS.Formation.FixedWing={}
@@ -216,23 +305,23 @@ ENUMS.Formation.FixedWing.FighterVic.Close = 917505
ENUMS.Formation.FixedWing.FighterVic.Open = 917506 ENUMS.Formation.FixedWing.FighterVic.Open = 917506
ENUMS.Formation.RotaryWing={} ENUMS.Formation.RotaryWing={}
ENUMS.Formation.RotaryWing.Column={} ENUMS.Formation.RotaryWing.Column={}
ENUMS.Formation.RotaryWing.Column.D70 = 720896 ENUMS.Formation.RotaryWing.Column.D70=720896
ENUMS.Formation.RotaryWing.Wedge={} ENUMS.Formation.RotaryWing.Wedge={}
ENUMS.Formation.RotaryWing.Wedge.D70 = 8 ENUMS.Formation.RotaryWing.Wedge.D70=8
ENUMS.Formation.RotaryWing.FrontRight={} ENUMS.Formation.RotaryWing.FrontRight={}
ENUMS.Formation.RotaryWing.FrontRight.D300 = 655361 ENUMS.Formation.RotaryWing.FrontRight.D300=655361
ENUMS.Formation.RotaryWing.FrontRight.D600 = 655362 ENUMS.Formation.RotaryWing.FrontRight.D600=655362
ENUMS.Formation.RotaryWing.FrontLeft={} ENUMS.Formation.RotaryWing.FrontLeft={}
ENUMS.Formation.RotaryWing.FrontLeft.D300 = 655617 ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
ENUMS.Formation.RotaryWing.FrontLeft.D600 = 655618 ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
ENUMS.Formation.RotaryWing.EchelonRight={} ENUMS.Formation.RotaryWing.EchelonRight={}
ENUMS.Formation.RotaryWing.EchelonRight.D70 = 589825 ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825
ENUMS.Formation.RotaryWing.EchelonRight.D300 = 589826 ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
ENUMS.Formation.RotaryWing.EchelonRight.D600 = 589827 ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
ENUMS.Formation.RotaryWing.EchelonLeft={} ENUMS.Formation.RotaryWing.EchelonLeft={}
ENUMS.Formation.RotaryWing.EchelonLeft.D70 = 590081 ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
ENUMS.Formation.RotaryWing.EchelonLeft.D300 = 590082 ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
ENUMS.Formation.RotaryWing.EchelonLeft.D600 = 590083 ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
ENUMS.Formation.Vehicle={} ENUMS.Formation.Vehicle={}
ENUMS.Formation.Vehicle.Vee="Vee" ENUMS.Formation.Vehicle.Vee="Vee"
ENUMS.Formation.Vehicle.EchelonRight="EchelonR" ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
@@ -244,34 +333,34 @@ ENUMS.Formation.Vehicle.Cone="Cone"
ENUMS.Formation.Vehicle.Diamond="Diamond" ENUMS.Formation.Vehicle.Diamond="Diamond"
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings. --- 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. -- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
-- @type ENUMS.FormationOld -- @type ENUMS.FormationOld
ENUMS.FormationOld={} ENUMS.FormationOld={}
ENUMS.FormationOld.FixedWing={} ENUMS.FormationOld.FixedWing={}
ENUMS.FormationOld.FixedWing.LineAbreast = 1 ENUMS.FormationOld.FixedWing.LineAbreast=1
ENUMS.FormationOld.FixedWing.Trail = 2 ENUMS.FormationOld.FixedWing.Trail=2
ENUMS.FormationOld.FixedWing.Wedge = 3 ENUMS.FormationOld.FixedWing.Wedge=3
ENUMS.FormationOld.FixedWing.EchelonRight = 4 ENUMS.FormationOld.FixedWing.EchelonRight=4
ENUMS.FormationOld.FixedWing.EchelonLeft = 5 ENUMS.FormationOld.FixedWing.EchelonLeft=5
ENUMS.FormationOld.FixedWing.FingerFour = 6 ENUMS.FormationOld.FixedWing.FingerFour=6
ENUMS.FormationOld.FixedWing.SpreadFour = 7 ENUMS.FormationOld.FixedWing.SpreadFour=7
ENUMS.FormationOld.FixedWing.BomberElement = 12 ENUMS.FormationOld.FixedWing.BomberElement=12
ENUMS.FormationOld.FixedWing.BomberElementHeight = 13 ENUMS.FormationOld.FixedWing.BomberElementHeight=13
ENUMS.FormationOld.FixedWing.FighterVic = 14 ENUMS.FormationOld.FixedWing.FighterVic=14
ENUMS.FormationOld.RotaryWing={} ENUMS.FormationOld.RotaryWing={}
ENUMS.FormationOld.RotaryWing.Wedge = 8 ENUMS.FormationOld.RotaryWing.Wedge=8
ENUMS.FormationOld.RotaryWing.Echelon = 9 ENUMS.FormationOld.RotaryWing.Echelon=9
ENUMS.FormationOld.RotaryWing.Front = 10 ENUMS.FormationOld.RotaryWing.Front=10
ENUMS.FormationOld.RotaryWing.Column = 11 ENUMS.FormationOld.RotaryWing.Column=11
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code). --- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
-- --
-- * Short pulse "*" -- * Short pulse "*"
-- * Long pulse "-" -- * Long pulse "-"
-- --
-- Pulses are separated by a blank character " ". -- Pulses are separated by a blank character " ".
-- --
-- @type ENUMS.Morse -- @type ENUMS.Morse
ENUMS.Morse={} ENUMS.Morse={}
ENUMS.Morse.A="* -" ENUMS.Morse.A="* -"
@@ -313,9 +402,9 @@ ENUMS.Morse.N0="- - - - -"
ENUMS.Morse[" "]=" " ENUMS.Morse[" "]=" "
--- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). --- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
-- --
-- @type ENUMS.ISOLang -- @type ENUMS.ISOLang
ENUMS.ISOLang = ENUMS.ISOLang =
{ {
Arabic = 'AR', Arabic = 'AR',
Chinese = 'ZH', Chinese = 'ZH',
@@ -329,7 +418,7 @@ ENUMS.ISOLang =
} }
--- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet). --- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet).
-- --
-- @type ENUMS.Phonetic -- @type ENUMS.Phonetic
ENUMS.Phonetic = ENUMS.Phonetic =
{ {
@@ -359,4 +448,114 @@ ENUMS.Phonetic =
X = 'Xray', X = 'Xray',
Y = 'Yankee', Y = 'Yankee',
Z = 'Zulu', Z = 'Zulu',
} }
--- Reporting Names (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_NATO_reporting_names_for_fighter_aircraft).
-- DCS known aircraft types
--
-- @type ENUMS.ReportingName
ENUMS.ReportingName =
{
NATO = {
-- Fighters
Dragon = "JF-17", -- China, correctly Fierce Dragon, Thunder for PAC
Fagot = "MiG-15",
Farmer = "MiG-19", -- Shenyang J-6 and Mikoyan-Gurevich MiG-19
Felon = "Su-57",
Fencer = "Su-24",
Fishbed = "MiG-21",
Fitter = "Su-17", -- Sukhoi Su-7 and Su-17/Su-20/Su-22
Flogger = "MiG-23", --and MiG-27
Flogger_D = "MiG-27", --and MiG-23
Flagon = "Su-15",
Foxbat = "MiG-25",
Fulcrum = "MiG-29",
Foxhound = "MiG-31",
Flanker = "Su-27", -- Sukhoi Su-27/Su-30/Su-33/Su-35/Su-37 and Shenyang J-11/J-15/J-16
Flanker_C = "Su-30",
Flanker_E = "Su-35",
Flanker_F = "Su-37",
Flanker_L = "J-11A",
Firebird = "J-10",
Sea_Flanker = "Su-33",
Fullback = "Su-34", -- also Su-32
Frogfoot = "Su-25",
Tomcat = "F-14", -- Iran
Mirage = "Mirage", -- various non-NATO
Codling = "Yak-40",
Maya = "L-39",
-- Fighters US/NATO
Warthog = "A-10",
--Mosquito = "A-20",
Skyhawk = "A-4E",
Viggen = "AJS37",
Harrier = "AV-8B",
Spirit = "B-2",
Aviojet = "C-101",
Nighthawk = "F-117A",
Eagle = "F-15C",
Mudhen = "F-15E",
Viper = "F-16",
Phantom = "F-4E",
Tiger = "F-5", -- was thinking to name this MiG-25 ;)
Sabre = "F-86",
Hornet = "A-18", -- avoiding the slash
Hawk = "Hawk",
Albatros = "L-39",
Goshawk = "T-45",
Starfighter = "F-104",
Tornado = "Tornado",
-- Transport / Bomber / Others
Atlas = "A400",
Lancer = "B1-B",
Stratofortress = "B-52H",
Hercules = "C-130",
Super_Hercules = "Hercules",
Globemaster = "C-17",
Greyhound = "C-2A",
Galaxy = "C-5",
Hawkeye = "E-2D",
Sentry = "E-3A",
Stratotanker = "KC-135",
Extender = "KC-10",
Orion = "P-3C",
Viking = "S-3B",
Osprey = "V-22",
-- Bomber Rus
Badger = "H6-J",
Bear_J = "Tu-142", -- also Tu-95
Bear = "Tu-95", -- also Tu-142
Blinder = "Tu-22",
Blackjack = "Tu-160",
-- AIC / Transport / Other
Clank = "An-30",
Curl = "An-26",
Candid = "IL-76",
Midas = "IL-78",
Mainstay = "A-50",
Mainring = "KJ-2000", -- A-50 China
Yak = "Yak-52",
-- Helos
Helix = "Ka-27",
Shark = "Ka-50",
Hind = "Mi-24",
Halo = "Mi-26",
Hip = "Mi-8",
Havoc = "Mi-28",
Gazelle = "SA342",
-- Helos US
Huey = "UH-1H",
Cobra = "AH-1",
Apache = "AH-64",
Chinook = "CH-47",
Sea_Stallion = "CH-53",
Kiowa = "OH-58",
Seahawk = "SH-60",
Blackhawk = "UH-60",
Sea_King = "S-61",
-- Drones
UCAV = "WingLoong",
Reaper = "MQ-9",
Predator = "MQ-1A",
}
}

View File

@@ -0,0 +1,773 @@
--- **UTILS** - ClassicFiFo Stack.
--
-- ===
--
-- ## Main Features:
--
-- * Build a simple multi-purpose FiFo (First-In, First-Out) stack for generic data.
-- * [Wikipedia](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
--
-- ===
--
-- ### Author: **applevangelist**
-- @module Utils.FiFo
-- @image MOOSE.JPG
-- Date: April 2022
do
--- FIFO class.
-- @type FIFO
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string version Version of FiFo
-- @field #number counter
-- @field #number pointer
-- @field #table stackbypointer
-- @field #table stackbyid
-- @extends Core.Base#BASE
---
-- @type FIFO.IDEntry
-- @field #number pointer
-- @field #table data
-- @field #table uniqueID
---
-- @field #FIFO
FIFO = {
ClassName = "FIFO",
lid = "",
version = "0.0.5",
counter = 0,
pointer = 0,
stackbypointer = {},
stackbyid = {}
}
--- Instantiate a new FIFO Stack
-- @param #FIFO self
-- @return #FIFO self
function FIFO:New()
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
self.pointer = 0
self.counter = 0
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
-- Set some string id for output to DCS.log file.
self.lid=string.format("%s (%s) | ", "FiFo", self.version)
self:T(self.lid .."Created.")
return self
end
--- Empty FIFO Stack
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Clear()
self:T(self.lid.."Clear")
self.pointer = 0
self.counter = 0
self.stackbypointer = nil
self.stackbyid = nil
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
return self
end
--- FIFO Push Object to Stack
-- @param #FIFO self
-- @param #table Object
-- @param #string UniqueID (optional) - will default to current pointer + 1. Note - if you intend to use `FIFO:GetIDStackSorted()` keep the UniqueID numerical!
-- @return #FIFO self
function FIFO:Push(Object,UniqueID)
self:T(self.lid.."Push")
self:T({Object,UniqueID})
self.pointer = self.pointer + 1
self.counter = self.counter + 1
local uniID = UniqueID
if not UniqueID then
self.uniquecounter = self.uniquecounter + 1
uniID = self.uniquecounter
end
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
return self
end
--- FIFO Pull Object from Stack
-- @param #FIFO self
-- @return #table Object or nil if stack is empty
function FIFO:Pull()
self:T(self.lid.."Pull")
if self.counter == 0 then return nil end
--local object = self.stackbypointer[self.pointer].data
--self.stackbypointer[self.pointer] = nil
local object = self.stackbypointer[1].data
self.stackbypointer[1] = nil
self.counter = self.counter - 1
--self.pointer = self.pointer - 1
self:Flatten()
return object
end
--- FIFO Pull Object from Stack by Pointer
-- @param #FIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty
function FIFO:PullByPointer(Pointer)
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
if self.counter == 0 then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
self.stackbypointer[Pointer] = nil
if object then self.stackbyid[object.uniqueID] = nil end
self.counter = self.counter - 1
self:Flatten()
if object then
return object.data
else
return nil
end
end
--- FIFO Read, not Pull, Object from Stack by Pointer
-- @param #FIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty or pointer does not exist
function FIFO:ReadByPointer(Pointer)
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- FIFO Read, not Pull, Object from Stack by UniqueID
-- @param #FIFO self
-- @param #number UniqueID
-- @return #table Object data or nil if stack is empty or ID does not exist
function FIFO:ReadByID(UniqueID)
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- FIFO Pull Object from Stack by UniqueID
-- @param #FIFO self
-- @param #tableUniqueID
-- @return #table Object or nil if stack is empty
function FIFO:PullByID(UniqueID)
self:T(self.lid.."PullByID " .. tostring(UniqueID))
if self.counter == 0 then return nil end
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
--self.stackbyid[UniqueID] = nil
if object then
return self:PullByPointer(object.pointer)
else
return nil
end
end
--- FIFO Housekeeping
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Flatten()
self:T(self.lid.."Flatten")
-- rebuild stacks
local pointerstack = {}
local idstack = {}
local counter = 0
for _ID,_entry in pairs(self.stackbypointer) do
counter = counter + 1
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
end
for _ID,_entry in pairs(pointerstack) do
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
end
self.stackbypointer = nil
self.stackbypointer = pointerstack
self.stackbyid = nil
self.stackbyid = idstack
self.counter = counter
self.pointer = counter
return self
end
--- FIFO Check Stack is empty
-- @param #FIFO self
-- @return #boolean empty
function FIFO:IsEmpty()
self:T(self.lid.."IsEmpty")
return self.counter == 0 and true or false
end
--- FIFO Get stack size
-- @param #FIFO self
-- @return #number size
function FIFO:GetSize()
self:T(self.lid.."GetSize")
return self.counter
end
--- FIFO Get stack size
-- @param #FIFO self
-- @return #number size
function FIFO:Count()
self:T(self.lid.."Count")
return self.counter
end
--- FIFO Check Stack is NOT empty
-- @param #FIFO self
-- @return #boolean notempty
function FIFO:IsNotEmpty()
self:T(self.lid.."IsNotEmpty")
return not self:IsEmpty()
end
--- FIFO Get the data stack by pointer
-- @param #FIFO self
-- @return #table Table of #FIFO.IDEntry entries
function FIFO:GetPointerStack()
self:T(self.lid.."GetPointerStack")
return self.stackbypointer
end
--- FIFO Check if a certain UniqeID exists
-- @param #FIFO self
-- @return #boolean exists
function FIFO:HasUniqueID(UniqueID)
self:T(self.lid.."HasUniqueID")
if self.stackbyid[UniqueID] ~= nil then
return true
else
return false
end
end
--- FIFO Get the data stack by UniqueID
-- @param #FIFO self
-- @return #table Table of #FIFO.IDEntry entries
function FIFO:GetIDStack()
self:T(self.lid.."GetIDStack")
return self.stackbyid
end
--- FIFO Get table of UniqueIDs sorted smallest to largest
-- @param #FIFO self
-- @return #table Table with index [1] to [n] of UniqueID entries
function FIFO:GetIDStackSorted()
self:T(self.lid.."GetIDStackSorted")
local stack = self:GetIDStack()
local idstack = {}
for _id,_entry in pairs(stack) do
idstack[#idstack+1] = _id
self:T({"pre",_id})
end
local function sortID(a, b)
return a < b
end
table.sort(idstack)
return idstack
end
--- FIFO Get table of data entries
-- @param #FIFO self
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
function FIFO:GetDataTable()
self:T(self.lid.."GetDataTable")
local datatable = {}
for _,_entry in pairs(self.stackbypointer) do
datatable[#datatable+1] = _entry.data
end
return datatable
end
--- FIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
-- @param #FIFO self
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
function FIFO:GetSortedDataTable()
self:T(self.lid.."GetSortedDataTable")
local datatable = {}
local idtablesorted = self:GetIDStackSorted()
for _,_entry in pairs(idtablesorted) do
datatable[#datatable+1] = self:ReadByID(_entry)
end
return datatable
end
--- Iterate the FIFO and call an iterator function for the given FIFO data, providing the object for each element of the stack and optional parameters.
-- @param #FIFO self
-- @param #function IteratorFunction The function that will be called.
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
-- @param #table FunctionArguments (Optional) Function arguments.
-- @return #FIFO self
function FIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
self:T(self.lid.."ForEach")
local Set = self:GetPointerStack() or {}
Arg = Arg or {}
local function CoRoutine()
local Count = 0
for ObjectID, ObjectData in pairs( Set ) do
local Object = ObjectData.data
self:T( {Object} )
if Function then
if Function( unpack( FunctionArguments or {} ), Object ) == true then
IteratorFunction( Object, unpack( Arg ) )
end
else
IteratorFunction( Object, unpack( Arg ) )
end
Count = Count + 1
end
return true
end
local co = CoRoutine
local function Schedule()
local status, res = co()
self:T( { status, res } )
if status == false then
error( res )
end
if res == false then
return true -- resume next time the loop
end
return false
end
Schedule()
return self
end
--- FIFO Print stacks to dcs.log
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Flush()
self:T(self.lid.."FiFo Flush")
self:I("FIFO Flushing Stack by Pointer")
for _id,_data in pairs (self.stackbypointer) do
local data = _data -- #FIFO.IDEntry
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("FIFO Flushing Stack by ID")
for _id,_data in pairs (self.stackbyid) do
local data = _data -- #FIFO.IDEntry
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("Counter = " .. self.counter)
self:I("Pointer = ".. self.pointer)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- End FIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- LIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
do
--- **UTILS** - LiFo Stack.
--
-- **Main Features:**
--
-- * Build a simple multi-purpose LiFo (Last-In, First-Out) stack for generic data.
--
-- ===
--
-- ### Author: **applevangelist**
--- LIFO class.
-- @type LIFO
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string version Version of LiFo
-- @field #number counter
-- @field #number pointer
-- @field #table stackbypointer
-- @field #table stackbyid
-- @extends Core.Base#BASE
---
-- @type LIFO.IDEntry
-- @field #number pointer
-- @field #table data
-- @field #table uniqueID
---
-- @field #LIFO
LIFO = {
ClassName = "LIFO",
lid = "",
version = "0.0.5",
counter = 0,
pointer = 0,
stackbypointer = {},
stackbyid = {}
}
--- Instantiate a new LIFO Stack
-- @param #LIFO self
-- @return #LIFO self
function LIFO:New()
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
self.pointer = 0
self.counter = 0
self.uniquecounter = 0
self.stackbypointer = {}
self.stackbyid = {}
-- Set some string id for output to DCS.log file.
self.lid=string.format("%s (%s) | ", "LiFo", self.version)
self:T(self.lid .."Created.")
return self
end
--- Empty LIFO Stack
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Clear()
self:T(self.lid.."Clear")
self.pointer = 0
self.counter = 0
self.stackbypointer = nil
self.stackbyid = nil
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
return self
end
--- LIFO Push Object to Stack
-- @param #LIFO self
-- @param #table Object
-- @param #string UniqueID (optional) - will default to current pointer + 1
-- @return #LIFO self
function LIFO:Push(Object,UniqueID)
self:T(self.lid.."Push")
self:T({Object,UniqueID})
self.pointer = self.pointer + 1
self.counter = self.counter + 1
local uniID = UniqueID
if not UniqueID then
self.uniquecounter = self.uniquecounter + 1
uniID = self.uniquecounter
end
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
return self
end
--- LIFO Pull Object from Stack
-- @param #LIFO self
-- @return #table Object or nil if stack is empty
function LIFO:Pull()
self:T(self.lid.."Pull")
if self.counter == 0 then return nil end
local object = self.stackbypointer[self.pointer].data
self.stackbypointer[self.pointer] = nil
--local object = self.stackbypointer[1].data
--self.stackbypointer[1] = nil
self.counter = self.counter - 1
self.pointer = self.pointer - 1
self:Flatten()
return object
end
--- LIFO Pull Object from Stack by Pointer
-- @param #LIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty
function LIFO:PullByPointer(Pointer)
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
if self.counter == 0 then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
self.stackbypointer[Pointer] = nil
if object then self.stackbyid[object.uniqueID] = nil end
self.counter = self.counter - 1
self:Flatten()
if object then
return object.data
else
return nil
end
end
--- LIFO Read, not Pull, Object from Stack by Pointer
-- @param #LIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty or pointer does not exist
function LIFO:ReadByPointer(Pointer)
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
local object = self.stackbypointer[Pointer] -- #LIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- LIFO Read, not Pull, Object from Stack by UniqueID
-- @param #LIFO self
-- @param #number UniqueID
-- @return #table Object or nil if stack is empty or ID does not exist
function LIFO:ReadByID(UniqueID)
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- LIFO Pull Object from Stack by UniqueID
-- @param #LIFO self
-- @param #tableUniqueID
-- @return #table Object or nil if stack is empty
function LIFO:PullByID(UniqueID)
self:T(self.lid.."PullByID " .. tostring(UniqueID))
if self.counter == 0 then return nil end
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
--self.stackbyid[UniqueID] = nil
if object then
return self:PullByPointer(object.pointer)
else
return nil
end
end
--- LIFO Housekeeping
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Flatten()
self:T(self.lid.."Flatten")
-- rebuild stacks
local pointerstack = {}
local idstack = {}
local counter = 0
for _ID,_entry in pairs(self.stackbypointer) do
counter = counter + 1
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
end
for _ID,_entry in pairs(pointerstack) do
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
end
self.stackbypointer = nil
self.stackbypointer = pointerstack
self.stackbyid = nil
self.stackbyid = idstack
self.counter = counter
self.pointer = counter
return self
end
--- LIFO Check Stack is empty
-- @param #LIFO self
-- @return #boolean empty
function LIFO:IsEmpty()
self:T(self.lid.."IsEmpty")
return self.counter == 0 and true or false
end
--- LIFO Get stack size
-- @param #LIFO self
-- @return #number size
function LIFO:GetSize()
self:T(self.lid.."GetSize")
return self.counter
end
--- LIFO Get stack size
-- @param #LIFO self
-- @return #number size
function LIFO:Count()
self:T(self.lid.."Count")
return self.counter
end
--- LIFO Check Stack is NOT empty
-- @param #LIFO self
-- @return #boolean notempty
function LIFO:IsNotEmpty()
self:T(self.lid.."IsNotEmpty")
return not self:IsEmpty()
end
--- LIFO Get the data stack by pointer
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetPointerStack()
self:T(self.lid.."GetPointerStack")
return self.stackbypointer
end
--- LIFO Get the data stack by UniqueID
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetIDStack()
self:T(self.lid.."GetIDStack")
return self.stackbyid
end
--- LIFO Get table of UniqueIDs sorted smallest to largest
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetIDStackSorted()
self:T(self.lid.."GetIDStackSorted")
local stack = self:GetIDStack()
local idstack = {}
for _id,_entry in pairs(stack) do
idstack[#idstack+1] = _id
self:T({"pre",_id})
end
local function sortID(a, b)
return a < b
end
table.sort(idstack)
return idstack
end
--- LIFO Check if a certain UniqeID exists
-- @param #LIFO self
-- @return #boolean exists
function LIFO:HasUniqueID(UniqueID)
self:T(self.lid.."HasUniqueID")
return self.stackbyid[UniqueID] and true or false
end
--- LIFO Print stacks to dcs.log
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Flush()
self:T(self.lid.."FiFo Flush")
self:I("LIFO Flushing Stack by Pointer")
for _id,_data in pairs (self.stackbypointer) do
local data = _data -- #LIFO.IDEntry
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("LIFO Flushing Stack by ID")
for _id,_data in pairs (self.stackbyid) do
local data = _data -- #LIFO.IDEntry
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("Counter = " .. self.counter)
self:I("Pointer = ".. self.pointer)
return self
end
--- LIFO Get table of data entries
-- @param #LIFO self
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
function LIFO:GetDataTable()
self:T(self.lid.."GetDataTable")
local datatable = {}
for _,_entry in pairs(self.stackbypointer) do
datatable[#datatable+1] = _entry.data
end
return datatable
end
--- LIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
-- @param #LIFO self
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
function LIFO:GetSortedDataTable()
self:T(self.lid.."GetSortedDataTable")
local datatable = {}
local idtablesorted = self:GetIDStackSorted()
for _,_entry in pairs(idtablesorted) do
datatable[#datatable+1] = self:ReadByID(_entry)
end
return datatable
end
--- Iterate the LIFO and call an iterator function for the given LIFO data, providing the object for each element of the stack and optional parameters.
-- @param #LIFO self
-- @param #function IteratorFunction The function that will be called.
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
-- @param #table FunctionArguments (Optional) Function arguments.
-- @return #LIFO self
function LIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
self:T(self.lid.."ForEach")
local Set = self:GetPointerStack() or {}
Arg = Arg or {}
local function CoRoutine()
local Count = 0
for ObjectID, ObjectData in pairs( Set ) do
local Object = ObjectData.data
self:T( {Object} )
if Function then
if Function( unpack( FunctionArguments or {} ), Object ) == true then
IteratorFunction( Object, unpack( Arg ) )
end
else
IteratorFunction( Object, unpack( Arg ) )
end
Count = Count + 1
end
return true
end
local co = CoRoutine
local function Schedule()
local status, res = co()
self:T( { status, res } )
if status == false then
error( res )
end
if res == false then
return true -- resume next time the loop
end
return false
end
Schedule()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- End LIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3770,3 +3770,44 @@ function POSITIONABLE:IsSubmarine()
return nil return nil
end end
--- Sets the controlled group to go at the specified speed in meters per second.
-- @param #CONTROLLABLE self
-- @param #number Speed Speed in meters per second.
-- @param #boolean Keep (Optional) When set to true, will maintain the speed on passing waypoints. If not present or false, the controlled group will return to the speed as defined by their route.
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetSpeed(Speed, Keep)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local speed = Speed or 5
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
Controller:setSpeed(speed, Keep)
end
end
return self
end
--- [AIR] Sets the controlled aircraft group to fly at the specified altitude in meters.
-- @param #CONTROLLABLE self
-- @param #number Altitude Altitude in meters.
-- @param #boolean Keep (Optional) When set to true, will maintain the altitude on passing waypoints. If not present or false, the controlled group will return to the altitude as defined by their route.
-- @param #string AltType (Optional) Specifies the altitude type used. If nil, the altitude type of the current waypoint will be used. Accepted values are "BARO" and "RADIO".
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetAltitude(Altitude, Keep, AltType)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local altitude = Altitude or 1000
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsAir() then
Controller:setAltitude(altitude, Keep, AltType)
end
end
end
return self
end

View File

@@ -384,7 +384,7 @@ end
-- So all event listeners will catch the destroy event of this group for each unit in the group. -- So all event listeners will catch the destroy event of this group for each unit in the group.
-- To raise these events, provide the `GenerateEvent` parameter. -- To raise these events, provide the `GenerateEvent` parameter.
-- @param #GROUP self -- @param #GROUP self
-- @param #boolean GenerateEvent If true, a crash or dead event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered. -- @param #boolean GenerateEvent If true, a crash [AIR] or dead [GROUND] event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered.
-- @param #number delay Delay in seconds before despawning the group. -- @param #number delay Delay in seconds before despawning the group.
-- @usage -- @usage
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group. -- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
@@ -622,7 +622,7 @@ function GROUP:GetUnits()
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
local DCSUnits = DCSGroup:getUnits() local DCSUnits = DCSGroup:getUnits() or {}
local Units = {} local Units = {}
for Index, UnitData in pairs( DCSUnits ) do for Index, UnitData in pairs( DCSUnits ) do
Units[#Units+1] = UNIT:Find( UnitData ) Units[#Units+1] = UNIT:Find( UnitData )
@@ -680,6 +680,30 @@ function GROUP:GetUnit( UnitNumber )
return nil return nil
end end
--- Check if an (air) group is a client or player slot. Information is retrieved from the group template.
-- @param #GROUP self
-- @return #boolean If true, group is associated with a client or player slot.
function GROUP:IsPlayer()
-- Get group.
-- local group=self:GetGroup()
-- Units of template group.
local units=self:GetTemplate().units
-- Get numbers.
for _,unit in pairs(units) do
-- Check if unit name matach and skill is Client or Player.
if unit.name==self:GetName() and (unit.skill=="Client" or unit.skill=="Player") then
return true
end
end
return false
end
--- Returns the DCS Unit with number UnitNumber. --- Returns the DCS Unit with number UnitNumber.
-- If the underlying DCS Unit does not exist, the method will return nil. . -- If the underlying DCS Unit does not exist, the method will return nil. .
-- @param #GROUP self -- @param #GROUP self
@@ -687,11 +711,25 @@ end
-- @return DCS#Unit The DCS Unit. -- @return DCS#Unit The DCS Unit.
function GROUP:GetDCSUnit( UnitNumber ) function GROUP:GetDCSUnit( UnitNumber )
local DCSGroup=self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
return DCSUnitFound local UnitFound = nil
-- 2.7.1 dead event bug, return the first alive unit instead
local units = DCSGroup:getUnits() or {}
for _,_unit in pairs(units) do
local UnitFound = UNIT:Find(_unit)
if UnitFound and UnitFound:IsAlive() then
return UnitFound
end
end
end end
return nil return nil
@@ -765,8 +803,7 @@ end
--- Returns the average velocity Vec3 vector. --- Returns the average velocity Vec3 vector.
-- @param Wrapper.Group#GROUP self -- @param Wrapper.Group#GROUP self
-- @return DCS#Vec3 The velocity Vec3 vector -- @return DCS#Vec3 The velocity Vec3 vector or `#nil` if the GROUP is not existing or alive.
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetVelocityVec3() function GROUP:GetVelocityVec3()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -797,11 +834,19 @@ function GROUP:GetVelocityVec3()
return nil return nil
end end
--- Returns the average group altitude in meters.
-- @param Wrapper.Group#GROUP self
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return #number The altitude of the group or nil if is not existing or alive.
function GROUP:GetAltitude(FromGround)
self:F2( self.GroupName )
return self:GetHeight(FromGround)
end
--- Returns the average group height in meters. --- Returns the average group height in meters.
-- @param Wrapper.Group#GROUP self -- @param Wrapper.Group#GROUP self
-- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level. -- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return DCS#Vec3 The height of the group or nil if is not existing or alive. -- @return #number The height of the group or nil if is not existing or alive.
function GROUP:GetHeight( FromGround ) function GROUP:GetHeight( FromGround )
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -901,6 +946,24 @@ function GROUP:GetTypeName()
return nil return nil
end end
--- [AIRPLANE] Get the NATO reporting name (platform, e.g. "Flanker") of a GROUP (note - first unit the group). "Bogey" if not found. Currently airplanes only!
--@param #GROUP self
--@return #string NatoReportingName or "Bogey" if unknown.
function GROUP:GetNatoReportingName()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
self:T3( GroupTypeName )
return UTILS.GetReportingName(GroupTypeName)
end
return "Bogey"
end
--- Gets the player name of the group. --- Gets the player name of the group.
-- @param #GROUP self -- @param #GROUP self
-- @return #string The player name of the group. -- @return #string The player name of the group.
@@ -994,24 +1057,32 @@ end
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP. -- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
function GROUP:GetCoordinate() function GROUP:GetCoordinate()
local FirstUnit = self:GetUnit(1) local Units = self:GetUnits() or {}
if FirstUnit then for _,_unit in pairs(Units) do
local FirstUnitCoordinate = FirstUnit:GetCoordinate() local FirstUnit = _unit -- Wrapper.Unit#UNIT
return FirstUnitCoordinate
if FirstUnit then
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
if FirstUnitCoordinate then
local Heading = self:GetHeading()
FirstUnitCoordinate.Heading = Heading
return FirstUnitCoordinate
end
end
end end
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } ) BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
return nil
end end
--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. --- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
-- @param #GROUP self -- @param #GROUP self
-- @param #number Radius -- @param #number Radius Radius in meters.
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP. -- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP or #nil The GROUP is invalid or empty.
-- @return #nil The GROUP is invalid or empty
-- @usage -- @usage
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP -- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
function GROUP:GetRandomVec3(Radius) function GROUP:GetRandomVec3(Radius)
@@ -1032,24 +1103,25 @@ end
--- Returns the mean heading of every UNIT in the GROUP in degrees --- Returns the mean heading of every UNIT in the GROUP in degrees
-- @param #GROUP self -- @param #GROUP self
-- @return #number mean heading of the GROUP -- @return #number Mean heading of the GROUP in degrees or #nil The first UNIT is not existing or alive.
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetHeading() function GROUP:GetHeading()
self:F2(self.GroupName) self:F2(self.GroupName)
self:F2(self.GroupName)
local GroupSize = self:GetSize() local GroupSize = self:GetSize()
local HeadingAccumulator = 0 local HeadingAccumulator = 0
local n=0 local n=0
local Units = self:GetUnits()
if GroupSize then if GroupSize then
for i = 1, GroupSize do for _,unit in pairs(Units) do
local unit=self:GetUnit(i)
if unit and unit:IsAlive() then if unit and unit:IsAlive() then
HeadingAccumulator = HeadingAccumulator + unit:GetHeading() HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
n=n+1 n=n+1
end end
end end
return math.floor(HeadingAccumulator / n) return math.floor(HeadingAccumulator / n)
end end
BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } ) BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } )
@@ -1061,8 +1133,8 @@ end
--- Return the fuel state and unit reference for the unit with the least --- Return the fuel state and unit reference for the unit with the least
-- amount of fuel in the group. -- amount of fuel in the group.
-- @param #GROUP self -- @param #GROUP self
-- @return #number The fuel state of the unit with the least amount of fuel -- @return #number The fuel state of the unit with the least amount of fuel.
-- @return #Unit reference to #Unit object for further processing -- @return Wrapper.Unit#UNIT reference to #Unit object for further processing.
function GROUP:GetFuelMin() function GROUP:GetFuelMin()
self:F3(self.ControllableName) self:F3(self.ControllableName)
@@ -1104,7 +1176,7 @@ function GROUP:GetFuelAvg()
local TotalFuel = 0 local TotalFuel = 0
for UnitID, UnitData in pairs( self:GetUnits() ) do for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
local UnitFuel = Unit:GetFuel() local UnitFuel = Unit:GetFuel() or 0
self:F( { Fuel = UnitFuel } ) self:F( { Fuel = UnitFuel } )
TotalFuel = TotalFuel + UnitFuel TotalFuel = TotalFuel + UnitFuel
end end
@@ -1181,13 +1253,14 @@ function GROUP:IsInZone( Zone )
for UnitID, UnitData in pairs(self:GetUnits()) do for UnitID, UnitData in pairs(self:GetUnits()) do
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
-- Get 2D vector. That's all we need for the zone check. local vec2 = nil
local vec2=Unit:GetVec2() if Unit then
-- Get 2D vector. That's all we need for the zone check.
vec2=Unit:GetVec2()
end
if Zone:IsVec2InZone(vec2) then if vec2 and Zone:IsVec2InZone(vec2) then
return true -- At least one unit is in the zone. That is enough. return true -- At least one unit is in the zone. That is enough.
else
-- This one is not but another could be.
end end
end end
@@ -2609,6 +2682,41 @@ function GROUP:GetSkill()
return skill return skill
end end
--- Get the unit in the group with the highest threat level, which is still alive.
-- @param #GROUP self
-- @return Wrapper.Unit#UNIT The most dangerous unit in the group.
-- @return #number Threat level of the unit.
function GROUP:GetHighestThreat()
-- Get units of the group.
local units=self:GetUnits()
if units then
local threat=nil ; local maxtl=0
for _,_unit in pairs(units or {}) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
-- Threat level of group.
local tl=unit:GetThreatLevel()
-- Check if greater the current threat.
if tl>maxtl then
maxtl=tl
threat=unit
end
end
end
return threat, maxtl
end
return nil, nil
end
--do -- Smoke --do -- Smoke
-- --
----- Signal a flare at the position of the GROUP. ----- Signal a flare at the position of the GROUP.

View File

@@ -382,7 +382,8 @@ function POSITIONABLE:GetCoordinate()
local PositionableVec3 = self:GetVec3() local PositionableVec3 = self:GetVec3()
local coord = COORDINATE:NewFromVec3( PositionableVec3 ) local coord = COORDINATE:NewFromVec3( PositionableVec3 )
local heading = self:GetHeading()
coord.Heading = heading
-- Return a new coordinate object. -- Return a new coordinate object.
return coord return coord
@@ -1176,6 +1177,30 @@ function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
return nil return nil
end end
--- Send a message to a @{Wrapper.Unit}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message.
-- @param Wrapper.Unit#UNIT MessageUnit The UNIT object receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToUnit( Message, Duration, MessageUnit, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
if MessageUnit:IsAlive() then
self:GetMessage( Message, Duration, Name ):ToUnit( MessageUnit )
else
BASE:E( { "Message not sent to Unit; Unit is not alive...", Message = Message, MessageUnit = MessageUnit } )
end
else
BASE:E( { "Message not sent to Unit; Positionable is not alive ...", Message = Message, Positionable = self, MessageUnit = MessageUnit } )
end
end
end
--- Send a message to a @{Wrapper.Group}. --- Send a message to a @{Wrapper.Group}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
@@ -1249,6 +1274,30 @@ function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Nam
return nil return nil
end end
--- Send a message to a @{Core.Set#SET_UNIT}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message.
-- @param Core.Set#SET_UNIT MessageSetUnit The SET_UNIT collection receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToSetUnit( Message, Duration, MessageSetUnit, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
MessageSetUnit:ForEachUnit(
function( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToUnit( MessageGroup )
end
)
end
end
return nil
end
--- Send a message to the players in the @{Wrapper.Group}. --- Send a message to the players in the @{Wrapper.Group}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self

View File

@@ -1,46 +1,52 @@
--- **Wrapper** -- STATIC wraps the DCS StaticObject class. --- **Wrapper** -- STATIC wraps the DCS StaticObject class.
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- --
-- ### Contributions: **funkyfranky** -- ### Contributions: **funkyfranky**
-- --
-- === -- ===
-- --
-- @module Wrapper.Static -- @module Wrapper.Static
-- @image Wrapper_Static.JPG -- @image Wrapper_Static.JPG
--- @type STATIC --- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle Static objects. --- Wrapper class to handle Static objects.
-- --
-- Note that Statics are almost the same as Units, but they don't have a controller. -- Note that Statics are almost the same as Units, but they don't have a controller.
-- The @{Wrapper.Static#STATIC} class is a wrapper class to handle the DCS Static objects: -- The @{Wrapper.Static#STATIC} class is a wrapper class to handle the DCS Static objects:
-- --
-- * Wraps the DCS Static objects. -- * Wraps the DCS Static objects.
-- * Support all DCS Static APIs. -- * Support all DCS Static APIs.
-- * Enhance with Static specific APIs not in the DCS API set. -- * Enhance with Static specific APIs not in the DCS API set.
-- --
-- ## STATIC reference methods -- ## STATIC reference methods
-- --
-- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object. -- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts). -- This is done at the beginning of the mission (when the mission starts).
-- --
-- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference -- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
-- using the Static Name. -- using the Static Name.
-- --
-- Another thing to know is that STATIC objects do not "contain" the DCS Static object. -- Another thing to know is that STATIC objects do not "contain" the DCS Static object.
-- The STATIc methods will reference the DCS Static object by name when it is needed during API execution. -- The STATIc methods will reference the DCS Static object by name when it is needed during API execution.
-- If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file. -- If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file.
-- --
-- The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance: -- The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance:
-- --
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name. -- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
-- --
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil). -- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
-- --
-- @field #STATIC -- @field #STATIC
STATIC = { ClassName = "STATIC" } STATIC = {
ClassName = "STATIC",
}
--- Register a static object. --- Register a static object.
-- @param #STATIC self -- @param #STATIC self
@@ -52,6 +58,7 @@ function STATIC:Register( StaticName )
return self return self
end end
--- Finds a STATIC from the _DATABASE using a DCSStatic object. --- Finds a STATIC from the _DATABASE using a DCSStatic object.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference. -- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
@@ -76,13 +83,13 @@ function STATIC:FindByName( StaticName, RaiseError )
-- Set static name. -- Set static name.
self.StaticName = StaticName self.StaticName = StaticName
if StaticFound then if StaticFound then
return StaticFound return StaticFound
end end
if RaiseError == nil or RaiseError == true then if RaiseError == nil or RaiseError == true then
error( "STATIC not found for: " .. StaticName ) error( "STATIC not found for: " .. StaticName )
end end
return nil return nil
@@ -90,39 +97,38 @@ end
--- Destroys the STATIC. --- Destroys the STATIC.
-- @param #STATIC self -- @param #STATIC self
-- @param #boolean GenerateEvent (Optional) true to generate a crash or dead event, false to not generate any event. `nil` (default) creates a remove event. -- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static.
-- @return #nil The DCS StaticObject is not existing or alive. -- @return #nil The DCS StaticObject is not existing or alive.
--
-- @usage -- @usage
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH. -- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
-- Helicopter = STATIC:FindByName( "Helicopter" ) -- Helicopter = STATIC:FindByName( "Helicopter" )
-- Helicopter:Destroy( true ) -- Helicopter:Destroy( true )
-- --
-- @usage -- @usage
-- -- Ground static example: destroy the static Tank and generate a S_EVENT_DEAD. -- -- Ground static example: destroy the static Tank and generate a S_EVENT_DEAD.
-- Tanks = UNIT:FindByName( "Tank" ) -- Tanks = UNIT:FindByName( "Tank" )
-- Tanks:Destroy( true ) -- Tanks:Destroy( true )
-- --
-- @usage -- @usage
-- -- Ship static example: destroy the Ship silently. -- -- Ship static example: destroy the Ship silently.
-- Ship = STATIC:FindByName( "Ship" ) -- Ship = STATIC:FindByName( "Ship" )
-- Ship:Destroy() -- Ship:Destroy()
-- --
-- @usage -- @usage
-- -- Destroy without event generation example. -- -- Destroy without event generation example.
-- Ship = STATIC:FindByName( "Boat" ) -- Ship = STATIC:FindByName( "Boat" )
-- Ship:Destroy( false ) -- Don't generate any event upon destruction. -- Ship:Destroy( false ) -- Don't generate an event upon destruction.
-- --
function STATIC:Destroy( GenerateEvent ) function STATIC:Destroy( GenerateEvent )
self:F2( self.ObjectName ) self:F2( self.ObjectName )
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
if DCSObject then if DCSObject then
local StaticName = DCSObject:getName() local StaticName = DCSObject:getName()
self:F( { StaticName = StaticName } ) self:F( { StaticName = StaticName } )
if GenerateEvent and GenerateEvent == true then if GenerateEvent and GenerateEvent == true then
if self:IsAir() then if self:IsAir() then
self:CreateEventCrash( timer.getTime(), DCSObject ) self:CreateEventCrash( timer.getTime(), DCSObject )
@@ -134,7 +140,7 @@ function STATIC:Destroy( GenerateEvent )
else else
self:CreateEventRemoveUnit( timer.getTime(), DCSObject ) self:CreateEventRemoveUnit( timer.getTime(), DCSObject )
end end
DCSObject:destroy() DCSObject:destroy()
return true return true
end end
@@ -142,16 +148,17 @@ function STATIC:Destroy( GenerateEvent )
return nil return nil
end end
--- Get DCS object of static of static. --- Get DCS object of static of static.
-- @param #STATIC self -- @param #STATIC self
-- @return DCS static object -- @return DCS static object
function STATIC:GetDCSObject() function STATIC:GetDCSObject()
local DCSStatic = StaticObject.getByName( self.StaticName ) local DCSStatic = StaticObject.getByName( self.StaticName )
if DCSStatic then if DCSStatic then
return DCSStatic return DCSStatic
end end
return nil return nil
end end
@@ -163,7 +170,7 @@ function STATIC:GetUnits()
local DCSStatic = self:GetDCSObject() local DCSStatic = self:GetDCSObject()
local Statics = {} local Statics = {}
if DCSStatic then if DCSStatic then
Statics[1] = STATIC:Find( DCSStatic ) Statics[1] = STATIC:Find( DCSStatic )
self:T3( Statics ) self:T3( Statics )
@@ -173,6 +180,7 @@ function STATIC:GetUnits()
return nil return nil
end end
--- Get threat level of static. --- Get threat level of static.
-- @param #STATIC self -- @param #STATIC self
-- @return #number Threat level 1. -- @return #number Threat level 1.
@@ -186,62 +194,65 @@ end
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @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 Heading The heading of the static respawn in degrees. Default is 0 deg.
-- @param #number Delay Delay in seconds before the static is spawned. -- @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 Heading=Heading or 0
if Delay and Delay > 0 then if Delay and Delay>0 then
SCHEDULER:New( nil, self.SpawnAt, { self, Coordinate, Heading }, Delay ) SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
else else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
end end
return self return self
end end
--- Respawn the @{Wrapper.Unit} at the same location with the same properties. --- Respawn the @{Wrapper.Unit} at the same location with the same properties.
-- This is useful to respawn a cargo after it has been destroyed. -- This is useful to respawn a cargo after it has been destroyed.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently. -- @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. -- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawn( CountryID, Delay ) function STATIC:ReSpawn(CountryID, Delay)
if Delay and Delay > 0 then if Delay and Delay>0 then
SCHEDULER:New( nil, self.ReSpawn, { self, CountryID }, Delay ) SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
else else
CountryID = CountryID or self:GetCountry() CountryID=CountryID or self:GetCountry()
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, CountryID )
SpawnStatic:Spawn( nil, self.StaticName )
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
SpawnStatic:Spawn(nil, self.StaticName)
end end
return self return self
end end
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading. --- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
-- @param #STATIC self -- @param #STATIC self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading. -- @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 is now. -- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawnAt( Coordinate, Heading, Delay ) function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
-- Heading=Heading or 0 --Heading=Heading or 0
if Delay and Delay > 0 then if Delay and Delay>0 then
SCHEDULER:New( nil, self.ReSpawnAt, { self, Coordinate, Heading }, Delay ) SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
else else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, self:GetCountry() )
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate( Coordinate, Heading, self.StaticName )
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
end end
return self return self
end end

View File

@@ -88,8 +88,9 @@
-- --
-- @field #UNIT UNIT -- @field #UNIT UNIT
UNIT = { UNIT = {
ClassName="UNIT", ClassName="UNIT",
UnitName=nil, UnitName=nil,
GroupName=nil,
} }
@@ -102,7 +103,7 @@ UNIT = {
-- Registration. -- Registration.
--- Create a new UNIT from DCSUnit. --- Create a new UNIT from DCSUnit.
-- @param #UNIT self -- @param #UNIT self
-- @param #string UnitName The name of the DCS unit. -- @param #string UnitName The name of the DCS unit.
@@ -110,11 +111,20 @@ UNIT = {
function UNIT:Register( UnitName ) function UNIT:Register( UnitName )
-- Inherit CONTROLLABLE. -- Inherit CONTROLLABLE.
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) --#UNIT
-- Set unit name. -- Set unit name.
self.UnitName = UnitName self.UnitName = UnitName
local unit=Unit.getByName(self.UnitName)
if unit then
local group = unit:getGroup()
if group then
self.GroupName=group:getName()
end
end
-- Set event prio. -- Set event prio.
self:SetEventPriority( 3 ) self:SetEventPriority( 3 )
@@ -168,6 +178,9 @@ function UNIT:GetDCSObject()
return nil return nil
end end
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. --- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
-- --
-- This function will: -- This function will:
@@ -260,6 +273,8 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
_DATABASE:Spawn( SpawnGroupTemplate ) _DATABASE:Spawn( SpawnGroupTemplate )
end end
--- Returns if the unit is activated. --- Returns if the unit is activated.
-- @param #UNIT self -- @param #UNIT self
-- @return #boolean `true` if Unit is activated. `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.
@@ -296,6 +311,8 @@ function UNIT:IsAlive()
return nil return nil
end end
--- Returns the Unit's callsign - the localized string. --- Returns the Unit's callsign - the localized string.
-- @param #UNIT self -- @param #UNIT self
-- @return #string The Callsign of the Unit. -- @return #string The Callsign of the Unit.
@@ -401,6 +418,17 @@ function UNIT:GetClient()
return nil return nil
end end
--- [AIRPLANE] Get the NATO reporting name of a UNIT. Currently airplanes only!
--@param #UNIT self
--@return #string NatoReportingName or "Bogey" if unknown.
function UNIT:GetNatoReportingName()
local typename = self:GetTypeName()
return UTILS.GetReportingName(typename)
end
--- Returns the unit's number in the group. --- Returns the unit's number in the group.
-- The number is the same number the unit has in ME. -- The number is the same number the unit has in ME.
-- It may not be changed during the mission. -- It may not be changed during the mission.
@@ -517,6 +545,63 @@ function UNIT:IsTanker()
return tanker, system return tanker, system
end end
--- Check if the unit can supply ammo. Currently, we have
--
-- * M 818
-- * Ural-375
-- * ZIL-135
--
-- This list needs to be extended, if DCS adds other units capable of supplying ammo.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply ammo.
function UNIT:IsAmmoSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M 818" then
-- Blue ammo truck.
return true
elseif typename=="Ural-375" then
-- Red ammo truck.
return true
elseif typename=="ZIL-135" then
-- Red ammo truck. Checked that it can also provide ammo.
return true
end
return false
end
--- Check if the unit can supply fuel. Currently, we have
--
-- * M978 HEMTT Tanker
-- * ATMZ-5
-- * ATMZ-10
-- * ATZ-5
--
-- This list needs to be extended, if DCS adds other units capable of supplying fuel.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply fuel.
function UNIT:IsFuelSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M978 HEMTT Tanker" then
return true
elseif typename=="ATMZ-5" then
return true
elseif typename=="ATMZ-10" then
return true
elseif typename=="ATZ-5" then
return true
end
return false
end
--- Returns the unit's group if it exist and nil otherwise. --- Returns the unit's group if it exist and nil otherwise.
-- @param Wrapper.Unit#UNIT self -- @param Wrapper.Unit#UNIT self
@@ -544,14 +629,14 @@ end
-- @return #string The name of the DCS Unit. -- @return #string The name of the DCS Unit.
-- @return #nil The DCS Unit is not existing or alive. -- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetPrefix() function UNIT:GetPrefix()
self:F2( self.UnitName ) self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject()
if DCSUnit then if DCSUnit then
local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 ) local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 )
self:T3( UnitPrefix ) self:T3( UnitPrefix )
return UnitPrefix return UnitPrefix
end end
return nil return nil
@@ -676,8 +761,6 @@ function UNIT:GetAmmunition()
return nammo, nshells, nrockets, nbombs, nmissiles return nammo, nshells, nrockets, nbombs, nmissiles
end end
--- Returns the unit sensors. --- Returns the unit sensors.
-- @param #UNIT self -- @param #UNIT self
-- @return DCS#Unit.Sensors Table of sensors. -- @return DCS#Unit.Sensors Table of sensors.
@@ -954,6 +1037,7 @@ end
-- @return #string Some text. -- @return #string Some text.
function UNIT:GetThreatLevel() function UNIT:GetThreatLevel()
local ThreatLevel = 0 local ThreatLevel = 0
local ThreatText = "" local ThreatText = ""
@@ -979,6 +1063,7 @@ function UNIT:GetThreatLevel()
"LR SAMs" "LR SAMs"
} }
if Attributes["LR SAM"] then ThreatLevel = 10 if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9 elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and elseif Attributes["SR SAM"] and
@@ -992,7 +1077,7 @@ function UNIT:GetThreatLevel()
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3 not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1 elseif Attributes["Infantry"] or Attributes["EWR"] then ThreatLevel = 1
end end
ThreatText = ThreatLevels[ThreatLevel+1] ThreatText = ThreatLevels[ThreatLevel+1]
@@ -1014,6 +1099,7 @@ function UNIT:GetThreatLevel()
"Fighter" "Fighter"
} }
if Attributes["Fighters"] then ThreatLevel = 10 if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9 elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8 elseif Attributes["Battleplanes"] then ThreatLevel = 8
@@ -1111,26 +1197,32 @@ end
-- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit. -- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit.
-- @return #nil The DCS Unit is not existing or alive. -- @return #nil The DCS Unit is not existing or alive.
function UNIT:OtherUnitInRadius( AwaitUnit, Radius ) function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } ) self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject()
if DCSUnit then if DCSUnit then
local UnitVec3 = self:GetVec3() local UnitVec3 = self:GetVec3()
local AwaitUnitVec3 = AwaitUnit:GetVec3() local AwaitUnitVec3 = AwaitUnit:GetVec3()
if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then
self:T3( "true" ) self:T3( "true" )
return true return true
else else
self:T3( "false" ) self:T3( "false" )
return false return false
end end
end end
return nil return nil
end end
--- Returns if the unit is a friendly unit. --- Returns if the unit is a friendly unit.
-- @param #UNIT self -- @param #UNIT self
-- @return #boolean IsFriendly evaluation result. -- @return #boolean IsFriendly evaluation result.

View File

@@ -5,6 +5,7 @@ Utilities/Enums.lua
Utilities/Profiler.lua Utilities/Profiler.lua
Utilities/Templates.lua Utilities/Templates.lua
Utilities/STTS.lua Utilities/STTS.lua
Utilities/FiFo.lua
Core/Base.lua Core/Base.lua
Core/Beacon.lua Core/Beacon.lua