mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaf77815ca | ||
|
|
3f488cc091 | ||
|
|
bdbbdfe60e | ||
|
|
d15c2be2d0 | ||
|
|
73994914eb | ||
|
|
63b0dae794 | ||
|
|
003e865ff7 | ||
|
|
e3c03287b7 | ||
|
|
73493c3a23 | ||
|
|
562a3f6208 | ||
|
|
3c5f3d6c37 | ||
|
|
a37d4214c0 | ||
|
|
636d6ce324 | ||
|
|
eef8b362d2 | ||
|
|
4ae586ebaa | ||
|
|
f6e673c2bb | ||
|
|
183a60159c | ||
|
|
1fdf4f371d | ||
|
|
f50c374d04 | ||
|
|
d59fc331f6 | ||
|
|
b83f478294 | ||
|
|
d5636f4a19 | ||
|
|
196bcf39cf | ||
|
|
afec1c3a5b | ||
|
|
6025339b46 | ||
|
|
40c6cc59d3 | ||
|
|
514e568e04 | ||
|
|
2f34b0a5ed | ||
|
|
708c076885 | ||
|
|
f0e0b918af | ||
|
|
e45f5e1122 | ||
|
|
932015668b | ||
|
|
4011bc3fe6 | ||
|
|
1dcccdc434 | ||
|
|
3380ed9360 | ||
|
|
ed9c14e63d | ||
|
|
4762793adc | ||
|
|
7df3946189 | ||
|
|
d5fb75fe43 | ||
|
|
c0b32a5584 | ||
|
|
e2b1276d7b | ||
|
|
f6aea13fae | ||
|
|
646b113c55 | ||
|
|
58074f499f | ||
|
|
1483ffd7ff | ||
|
|
cdaef851a0 | ||
|
|
04068d7117 | ||
|
|
41e8ddea8c | ||
|
|
cc49791997 | ||
|
|
ba4a8050ba | ||
|
|
7c5067a59a | ||
|
|
69eb920173 | ||
|
|
07d761941a | ||
|
|
ca52585759 | ||
|
|
decc9d09f8 | ||
|
|
466a18447c | ||
|
|
27902ee107 | ||
|
|
8a8b806362 | ||
|
|
40bb181c78 | ||
|
|
8099847e29 | ||
|
|
6e8edd95ec | ||
|
|
5112c9598b | ||
|
|
749158c086 | ||
|
|
b3d4024f21 | ||
|
|
c283b66c1d | ||
|
|
3209843318 | ||
|
|
d35e5cc0f7 | ||
|
|
e5eeb592a2 | ||
|
|
3d38f4d17a | ||
|
|
2d91647e0b | ||
|
|
cac0f30673 | ||
|
|
c5ecba3389 | ||
|
|
e0397dff47 | ||
|
|
e08fb2e972 | ||
|
|
c02ae82003 | ||
|
|
e6fc301b0d | ||
|
|
a385ed57fb | ||
|
|
09dafe4b1d | ||
|
|
ba8505c983 | ||
|
|
fba359d389 | ||
|
|
c56763b68f | ||
|
|
03c2943545 | ||
|
|
98039b9048 | ||
|
|
5065d3b068 | ||
|
|
6828f7e262 | ||
|
|
a685f3ffbd | ||
|
|
e84156d2e9 | ||
|
|
0ce1c31e1c | ||
|
|
f7e14bb60c | ||
|
|
7b907df816 | ||
|
|
7f5be2829c | ||
|
|
fa5afae783 | ||
|
|
b17507d0fa | ||
|
|
5ed43a3190 | ||
|
|
d2a5144a23 | ||
|
|
fb2031d7ca | ||
|
|
4a42571925 | ||
|
|
38413625c2 | ||
|
|
2d544b7a98 | ||
|
|
b1e5e1840e | ||
|
|
e6f9b4a125 | ||
|
|
5260b2b430 | ||
|
|
0213bc7aef | ||
|
|
ca8b0899d0 | ||
|
|
a1f5c0ab9b | ||
|
|
b0e3f82d27 | ||
|
|
327ab4766b | ||
|
|
3aee8a49c1 | ||
|
|
57de0b7351 | ||
|
|
4df1e310a3 | ||
|
|
802a77238a | ||
|
|
85a7e18fae | ||
|
|
26b1fd3487 | ||
|
|
ae7a363012 | ||
|
|
473362af45 | ||
|
|
cde0d09f0a | ||
|
|
94f093826b | ||
|
|
84f231ea08 | ||
|
|
3d9bb14713 | ||
|
|
6c6cdcf763 | ||
|
|
00c8690e61 | ||
|
|
a0d492cd2d | ||
|
|
ba5ccc1021 | ||
|
|
a4163017d5 | ||
|
|
7f4a5c48ec | ||
|
|
9f7588b245 | ||
|
|
63cbc0c55b | ||
|
|
28eb7a678c | ||
|
|
a95c49915a | ||
|
|
b7adc6add6 | ||
|
|
2aeebf280b | ||
|
|
8ac06979f0 | ||
|
|
2d4f90d5eb | ||
|
|
d7a44a639d | ||
|
|
7bfa05f47d | ||
|
|
c7bbb09195 | ||
|
|
41c9c15ae5 | ||
|
|
964831becf | ||
|
|
e847b92cce | ||
|
|
c2ecd86bb4 | ||
|
|
70d922fad6 |
10
.vs/VSWorkspaceState.json
Normal file
10
.vs/VSWorkspaceState.json
Normal 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
BIN
.vs/slnx.sqlite
Normal file
Binary file not shown.
@@ -425,7 +425,7 @@ do -- AI_A2A_DISPATCHER
|
||||
-- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield.
|
||||
-- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield.
|
||||
--
|
||||
-- **The default landing method is to spawn new aircraft directly in the air.**
|
||||
-- **The default take-off method is to spawn new aircraft directly in the air.**
|
||||
--
|
||||
-- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency.
|
||||
-- The more and the longer aircraft need to taxi at an airfield, the more risk there is that:
|
||||
@@ -1510,7 +1510,7 @@ do -- AI_A2A_DISPATCHER
|
||||
local Message = "Clearing (" .. DefenderTask.Type .. ") "
|
||||
Message = Message .. Defender:GetName()
|
||||
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
|
||||
self:F( { Target = Message } )
|
||||
end
|
||||
@@ -1559,7 +1559,7 @@ do -- AI_A2A_DISPATCHER
|
||||
|
||||
local Message = "(" .. self.DefenderTasks[Defender].Type .. ") "
|
||||
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 } )
|
||||
if AttackerDetection then
|
||||
self.DefenderTasks[Defender].Target = AttackerDetection
|
||||
@@ -2653,7 +2653,7 @@ do -- AI_A2A_DISPATCHER
|
||||
-- -- Let flights by default land and despawn at engine shutdown.
|
||||
-- A2ADispatcher:SetDefaultLandingAtEngineShutdown()
|
||||
--
|
||||
function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown()
|
||||
function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown()
|
||||
|
||||
self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown )
|
||||
|
||||
@@ -2971,7 +2971,20 @@ do -- AI_A2A_DISPATCHER
|
||||
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.
|
||||
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
|
||||
-- Ok, so we have a friendly near the potential target.
|
||||
-- Now we need to check if the AIGroup has a Task.
|
||||
@@ -3570,7 +3583,7 @@ do -- AI_A2A_DISPATCHER
|
||||
end
|
||||
|
||||
--- 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 )
|
||||
|
||||
local detection = self.Detection -- Functional.Detection#DETECTION_AREAS
|
||||
@@ -3876,7 +3889,31 @@ do
|
||||
function AI_A2A_DISPATCHER:SchedulerCAP( SquadronName )
|
||||
self:CAP( SquadronName )
|
||||
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
|
||||
|
||||
do
|
||||
|
||||
@@ -4728,6 +4728,30 @@ do
|
||||
local PatrolTaskType = PatrolTaskTypes[math.random(1,3)]
|
||||
self:Patrol( SquadronName, PatrolTaskType )
|
||||
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
|
||||
|
||||
|
||||
@@ -253,6 +253,9 @@ function AI_AIR:New( AIGroup )
|
||||
|
||||
self.IdleCount = 0
|
||||
|
||||
self.RTBSpeedMaxFactor = 0.6
|
||||
self.RTBSpeedMinFactor = 0.5
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -576,6 +579,19 @@ function AI_AIR.RTBHold( AIGroup, Fsm )
|
||||
|
||||
end
|
||||
|
||||
--- Set the min and max factors on RTB speed. Use this, if your planes are heading back to base too fast. Default values are 0.5 and 0.6.
|
||||
-- The RTB speed is calculated as the max speed of the unit multiplied by MinFactor (lower bracket) and multiplied by MaxFactor (upper bracket).
|
||||
-- A random value in this bracket is then applied in the waypoint routing generation.
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number MinFactor Lower bracket factor. Defaults to 0.5.
|
||||
-- @param #number MaxFactor Upper bracket factor. Defaults to 0.6.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
|
||||
self.RTBSpeedMaxFactor = MaxFactor or 0.6
|
||||
self.RTBSpeedMinFactor = MinFactor or 0.5
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
@@ -589,7 +605,9 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
self:ClearTargetDistance()
|
||||
--AIGroup:ClearTasks()
|
||||
|
||||
|
||||
AIGroup:OptionProhibitAfterburner(true)
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
@@ -597,12 +615,14 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
local FromCoord = AIGroup:GetCoordinate()
|
||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
|
||||
local ToTargetVec3 = ToTargetCoord:GetVec3()
|
||||
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground
|
||||
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground
|
||||
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
|
||||
|
||||
if not self.RTBMinSpeed or not self.RTBMaxSpeed then
|
||||
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
||||
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 )
|
||||
local RTBSpeedMaxFactor = self.RTBSpeedMaxFactor or 0.6
|
||||
local RTBSpeedMinFactor = self.RTBSpeedMinFactor or 0.5
|
||||
self:SetRTBSpeed( RTBSpeedMax * RTBSpeedMinFactor, RTBSpeedMax * RTBSpeedMaxFactor)
|
||||
end
|
||||
|
||||
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
|
||||
|
||||
@@ -533,6 +533,10 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
|
||||
if not TargetCoord then
|
||||
self:Return()
|
||||
return
|
||||
end
|
||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
||||
|
||||
@@ -515,8 +515,8 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
--- Calculate the current route point.
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
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 ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -428,8 +428,12 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
--- Calculate the current route point.
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
if not CurrentVec2 then -- flight dead at this point
|
||||
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 ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -460,7 +460,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
|
||||
--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 ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -726,7 +726,8 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
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.
|
||||
-- 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
|
||||
self:T( "Not in the air, finding route path within PatrolZone" )
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
if not CurrentVec2 then return 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 ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
@@ -758,8 +760,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
else
|
||||
self:T( "In the air, finding route path within PatrolZone" )
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
if not CurrentVec2 then return 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 ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
@@ -870,9 +873,10 @@ function AI_PATROL_ZONE:onafterRTB()
|
||||
|
||||
--- Calculate the current route point.
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
if not CurrentVec2 then return end
|
||||
--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 ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
|
||||
@@ -731,13 +731,14 @@ end
|
||||
-- @param #BASE self
|
||||
-- @param DCS#Time EventTime The time stamp of the event.
|
||||
-- @param DCS#Object Initiator The initiating object of the event.
|
||||
function BASE:CreateEventCrash( EventTime, Initiator )
|
||||
function BASE:CreateEventCrash( EventTime, Initiator, IniObjectCategory )
|
||||
self:F( { EventTime, Initiator } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_CRASH,
|
||||
time = EventTime,
|
||||
initiator = Initiator,
|
||||
IniObjectCategory = IniObjectCategory,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
--- **Core** - TACAN and other beacons.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Provide beacon functionality to assist pilots.
|
||||
--
|
||||
-- ===
|
||||
@@ -14,34 +14,34 @@
|
||||
-- @image Core_Radio.JPG
|
||||
|
||||
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
|
||||
--
|
||||
--
|
||||
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
||||
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon.
|
||||
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
||||
-- attach to a cargo crate, for exemple.
|
||||
--
|
||||
-- ## AA TACAN Beacon usage
|
||||
--
|
||||
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon.
|
||||
-- Use @#BEACON:StopAATACAN}() to stop it.
|
||||
--
|
||||
-- There are two types of BEACONs available : the (aircraft) TACAN Beacon and the general purpose Radio Beacon.
|
||||
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very useful to simulate the battery time if your BEACON is
|
||||
-- attach to a cargo crate, for exemple.
|
||||
--
|
||||
-- ## Aircraft TACAN Beacon usage
|
||||
--
|
||||
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON.ActivateTACAN}() to set the beacon parameters and start the beacon.
|
||||
-- Use @{#BEACON.StopRadioBeacon}() to stop it.
|
||||
--
|
||||
-- ## General Purpose Radio Beacon usage
|
||||
--
|
||||
--
|
||||
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
||||
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
|
||||
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
||||
--
|
||||
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON.RadioBeacon}() to set the beacon parameters and start the beacon.
|
||||
-- Use @{#BEACON.StopRadioBeacon}() to stop it.
|
||||
--
|
||||
-- @type BEACON
|
||||
-- @field #string ClassName Name of the class "BEACON".
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
|
||||
-- @extends Core.Base#BASE
|
||||
BEACON = {
|
||||
ClassName = "BEACON",
|
||||
ClassName = "BEACON",
|
||||
Positionable = nil,
|
||||
name = nil,
|
||||
name = nil,
|
||||
}
|
||||
|
||||
--- Beacon types supported by DCS.
|
||||
--- Beacon types supported by DCS.
|
||||
-- @type BEACON.Type
|
||||
-- @field #number NULL
|
||||
-- @field #number VOR
|
||||
@@ -65,19 +65,19 @@ BEACON = {
|
||||
-- @field #number ICLS_GLIDESLOPE
|
||||
-- @field #number NAUTICAL_HOMER
|
||||
BEACON.Type={
|
||||
NULL = 0,
|
||||
NULL = 0,
|
||||
VOR = 1,
|
||||
DME = 2,
|
||||
VOR_DME = 3,
|
||||
VOR_DME = 3,
|
||||
TACAN = 4,
|
||||
VORTAC = 5,
|
||||
VORTAC = 5,
|
||||
RSBN = 128,
|
||||
BROADCAST_STATION = 1024,
|
||||
BROADCAST_STATION = 1024,
|
||||
HOMER = 8,
|
||||
AIRPORT_HOMER = 4104,
|
||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||
AIRPORT_HOMER = 4104,
|
||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||
ILS_FAR_HOMER = 16408,
|
||||
ILS_NEAR_HOMER = 16424,
|
||||
ILS_NEAR_HOMER = 16424,
|
||||
ILS_LOCALIZER = 16640,
|
||||
ILS_GLIDESLOPE = 16896,
|
||||
PRMG_LOCALIZER = 33024,
|
||||
@@ -95,26 +95,26 @@ BEACON.Type={
|
||||
-- @field #number TACAN TACtical Air Navigation system on ground.
|
||||
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
|
||||
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
||||
-- @field #number VOR Very High Frequency Omnidirectional Range
|
||||
-- @field #number VOR Very High Frequency Omni-Directional Range
|
||||
-- @field #number ILS_LOCALIZER ILS localizer
|
||||
-- @field #number ILS_GLIDESLOPE ILS glide slope.
|
||||
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
||||
-- @field #number PRGM_GLIDESLOPE PRGM glide slope.
|
||||
-- @field #number BROADCAST_STATION Broadcast station.
|
||||
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
||||
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omni-directional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
||||
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
||||
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
||||
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
||||
-- @field #number ICLS_LOCALIZER Carrier landing system.
|
||||
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
|
||||
BEACON.System={
|
||||
PAR_10 = 1,
|
||||
RSBN_5 = 2,
|
||||
TACAN = 3,
|
||||
PAR_10 = 1,
|
||||
RSBN_5 = 2,
|
||||
TACAN = 3,
|
||||
TACAN_TANKER_X = 4,
|
||||
TACAN_TANKER_Y = 5,
|
||||
VOR = 6,
|
||||
ILS_LOCALIZER = 7,
|
||||
VOR = 6,
|
||||
ILS_LOCALIZER = 7,
|
||||
ILS_GLIDESLOPE = 8,
|
||||
PRMG_LOCALIZER = 9,
|
||||
PRMG_GLIDESLOPE = 10,
|
||||
@@ -131,27 +131,28 @@ BEACON.System={
|
||||
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
|
||||
-- @param #BEACON self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||
-- @return #BEACON Beacon object or #nil if the POSITIONABLE is invalid.
|
||||
function BEACON:New( Positionable )
|
||||
-- @return #BEACON Beacon object or #nil if the positionable is invalid.
|
||||
function BEACON:New(Positionable)
|
||||
|
||||
-- Inherit BASE.
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #BEACON
|
||||
|
||||
local self=BASE:Inherit(self, BASE:New()) --#BEACON
|
||||
|
||||
-- Debug.
|
||||
self:F( Positionable )
|
||||
|
||||
self:F(Positionable)
|
||||
|
||||
-- Set positionable.
|
||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure POSITIONABLE is valid
|
||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
||||
self.Positionable = Positionable
|
||||
self.name = Positionable:GetName()
|
||||
self:I( string.format( "New BEACON %s", tostring( self.name ) ) )
|
||||
self.name=Positionable:GetName()
|
||||
self:I(string.format("New BEACON %s", tostring(self.name)))
|
||||
return self
|
||||
end
|
||||
|
||||
self:E( { "The passed POSITIONABLE is invalid, no BEACON created", Positionable } )
|
||||
|
||||
self:E({"The passed positionable is invalid, no BEACON created", Positionable})
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Activates a TACAN BEACON.
|
||||
-- @param #BEACON self
|
||||
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
|
||||
@@ -161,55 +162,60 @@ end
|
||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Let's create a TACAN Beacon for a tanker
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||
--
|
||||
--
|
||||
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
|
||||
--
|
||||
function BEACON:ActivateTACAN( Channel, Mode, Message, Bearing, Duration )
|
||||
self:T( { channel = Channel, mode = Mode, callsign = Message, bearing = Bearing, duration = Duration } )
|
||||
|
||||
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
||||
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
||||
|
||||
Mode=Mode or "Y"
|
||||
|
||||
-- Get frequency.
|
||||
local Frequency = UTILS.TACANToFrequency( Channel, Mode )
|
||||
|
||||
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
||||
|
||||
-- Check.
|
||||
if not Frequency then
|
||||
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } )
|
||||
if not Frequency then
|
||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-- Beacon type.
|
||||
local Type = BEACON.Type.TACAN
|
||||
|
||||
local Type=BEACON.Type.TACAN
|
||||
|
||||
-- Beacon system.
|
||||
local System = BEACON.System.TACAN
|
||||
|
||||
local System=BEACON.System.TACAN
|
||||
|
||||
-- Check if unit is an aircraft and set system accordingly.
|
||||
local AA = self.Positionable:IsAir()
|
||||
local AA=self.Positionable:IsAir()
|
||||
|
||||
|
||||
if AA then
|
||||
System = 5 -- NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
|
||||
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
|
||||
-- Check if "Y" mode is selected for aircraft.
|
||||
if Mode ~= "Y" then
|
||||
self:E( { "WARNING: The POSITIONABLE you want to attach the AA TACAN Beacon is an aircraft: Mode should Y! The BEACON is not emitting.", self.Positionable } )
|
||||
if Mode=="X" then
|
||||
--self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y!", self.Positionable})
|
||||
System=BEACON.System.TACAN_TANKER_X
|
||||
else
|
||||
System=BEACON.System.TACAN_TANKER_Y
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Attached unit.
|
||||
local UnitID = self.Positionable:GetID()
|
||||
|
||||
local UnitID=self.Positionable:GetID()
|
||||
|
||||
-- Debug.
|
||||
self:I( { string.format( "BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring( self.name ), Channel, Mode, Message, tostring( Bearing ), tostring( Duration ) ) } )
|
||||
|
||||
self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))})
|
||||
|
||||
-- Start beacon.
|
||||
self.Positionable:CommandActivateBeacon( Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing )
|
||||
|
||||
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
||||
|
||||
-- Stop scheduler.
|
||||
if Duration then
|
||||
self.Positionable:DeactivateBeacon( Duration )
|
||||
self.Positionable:DeactivateBeacon(Duration)
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -219,27 +225,55 @@ end
|
||||
-- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon.
|
||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
function BEACON:ActivateICLS( Channel, Callsign, Duration )
|
||||
self:F( { Channel = Channel, Callsign = Callsign, Duration = Duration } )
|
||||
|
||||
function BEACON:ActivateICLS(Channel, Callsign, Duration)
|
||||
self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
|
||||
|
||||
-- Attached unit.
|
||||
local UnitID = self.Positionable:GetID()
|
||||
|
||||
local UnitID=self.Positionable:GetID()
|
||||
|
||||
-- Debug
|
||||
self:T2( { "ICLS BEACON started!" } )
|
||||
|
||||
self:T2({"ICLS BEACON started!"})
|
||||
|
||||
-- Start beacon.
|
||||
self.Positionable:CommandActivateICLS( Channel, UnitID, Callsign )
|
||||
|
||||
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
|
||||
|
||||
-- Stop scheduler
|
||||
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
self.Positionable:DeactivateBeacon( Duration )
|
||||
self.Positionable:DeactivateBeacon(Duration)
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Activates a TACAN BEACON on an Aircraft.
|
||||
--- Activates a LINK4 BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
|
||||
-- @param #BEACON self
|
||||
-- @param #number Frequency LINK4 FRequency in MHz, eg 336.
|
||||
-- @param #string Morse The ID that is going to be coded in Morse and broadcasted by the beacon.
|
||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
function BEACON:ActivateLink4(Frequency, Morse, Duration)
|
||||
self:F({Frequency=Frequency, Morse=Morse, Duration=Duration})
|
||||
|
||||
-- Attached unit.
|
||||
local UnitID=self.Positionable:GetID()
|
||||
|
||||
-- Debug
|
||||
self:T2({"LINK4 BEACON started!"})
|
||||
|
||||
-- Start beacon.
|
||||
self.Positionable:CommandActivateLink4(Frequency,UnitID,Morse)
|
||||
|
||||
-- Stop sheduler
|
||||
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
self.Positionable:DeactivateLink4(Duration)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- DEPRECATED: Please use @{BEACON:ActivateTACAN}() instead.
|
||||
-- Activates a TACAN BEACON on an Aircraft.
|
||||
-- @param #BEACON self
|
||||
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
|
||||
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
|
||||
@@ -247,57 +281,58 @@ end
|
||||
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Let's create a TACAN Beacon for a tanker
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||
--
|
||||
--
|
||||
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
|
||||
--
|
||||
function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration )
|
||||
self:F( { TACANChannel, Message, Bearing, BeaconDuration } )
|
||||
|
||||
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
||||
self:F({TACANChannel, Message, Bearing, BeaconDuration})
|
||||
|
||||
local IsValid = true
|
||||
|
||||
|
||||
if not self.Positionable:IsAir() then
|
||||
self:E( { "The POSITIONABLE you want to attach the AA TACAN Beacon is not an aircraft! The BEACON is not emitting", self.Positionable } )
|
||||
self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
|
||||
IsValid = false
|
||||
end
|
||||
|
||||
local Frequency = self:_TACANToFrequency( TACANChannel, "Y" )
|
||||
if not Frequency then
|
||||
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } )
|
||||
|
||||
local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
|
||||
if not Frequency then
|
||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
||||
IsValid = false
|
||||
end
|
||||
|
||||
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
|
||||
-- or 14 (TACAN_AA_MODE_Y) if it does not
|
||||
|
||||
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not
|
||||
local System
|
||||
if Bearing then
|
||||
System = 5
|
||||
System = BEACON.System.TACAN_TANKER_Y
|
||||
else
|
||||
System = 14
|
||||
System = BEACON.System.TACAN_AA_MODE_Y
|
||||
end
|
||||
|
||||
|
||||
if IsValid then -- Starts the BEACON
|
||||
self:T2( { "AA TACAN BEACON started !" } )
|
||||
self.Positionable:SetCommand( {
|
||||
self:T2({"AA TACAN BEACON started !"})
|
||||
self.Positionable:SetCommand({
|
||||
id = "ActivateBeacon",
|
||||
params = {
|
||||
type = 4,
|
||||
type = BEACON.Type.TACAN,
|
||||
system = System,
|
||||
callsign = Message,
|
||||
AA = true,
|
||||
frequency = Frequency,
|
||||
},
|
||||
} )
|
||||
|
||||
bearing = Bearing,
|
||||
modeChannel = "Y",
|
||||
}
|
||||
})
|
||||
|
||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
SCHEDULER:New( nil, function()
|
||||
SCHEDULER:New(nil,
|
||||
function()
|
||||
self:StopAATACAN()
|
||||
end, {}, BeaconDuration )
|
||||
end, {}, BeaconDuration)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -307,19 +342,21 @@ end
|
||||
function BEACON:StopAATACAN()
|
||||
self:F()
|
||||
if not self.Positionable then
|
||||
self:E( { "Start the beacon first before stopping it!" } )
|
||||
self:E({"Start the beacon first before stoping it !"})
|
||||
else
|
||||
self.Positionable:SetCommand( {
|
||||
id = 'DeactivateBeacon',
|
||||
params = {},
|
||||
} )
|
||||
self.Positionable:SetCommand({
|
||||
id = 'DeactivateBeacon',
|
||||
params = {
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Activates a general purpose Radio Beacon
|
||||
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
|
||||
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8.
|
||||
-- They can home in on these specific frequencies :
|
||||
-- Although any frequency could be used, only a few DCS Modules can home on radio beacons at the time of writing, i.e. the Mi-8, Huey, Gazelle etc.
|
||||
-- The following e.g. can home in on these specific frequencies :
|
||||
-- * **Mi8**
|
||||
-- * R-828 -> 20-60MHz
|
||||
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
||||
@@ -342,63 +379,64 @@ end
|
||||
--
|
||||
-- -- Set the beacon and start it
|
||||
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
|
||||
function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDuration )
|
||||
self:F( { FileName, Frequency, Modulation, Power, BeaconDuration } )
|
||||
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
|
||||
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
|
||||
local IsValid = false
|
||||
|
||||
|
||||
-- Check the filename
|
||||
if type( FileName ) == "string" then
|
||||
if FileName:find( ".ogg" ) or FileName:find( ".wav" ) then
|
||||
if not FileName:find( "l10n/DEFAULT/" ) then
|
||||
if type(FileName) == "string" then
|
||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
||||
if not FileName:find("l10n/DEFAULT/") then
|
||||
FileName = "l10n/DEFAULT/" .. FileName
|
||||
end
|
||||
IsValid = true
|
||||
end
|
||||
end
|
||||
if not IsValid then
|
||||
self:E( { "File name invalid. Maybe something wrong with the extension? ", FileName } )
|
||||
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
|
||||
end
|
||||
|
||||
|
||||
-- Check the Frequency
|
||||
if type( Frequency ) ~= "number" and IsValid then
|
||||
self:E( { "Frequency invalid. ", Frequency } )
|
||||
if type(Frequency) ~= "number" and IsValid then
|
||||
self:E({"Frequency invalid. ", Frequency})
|
||||
IsValid = false
|
||||
end
|
||||
Frequency = Frequency * 1000000 -- Conversion to Hz
|
||||
|
||||
|
||||
-- Check the modulation
|
||||
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then -- TODO: Maybe make this future proof if ED decides to add an other modulation ?
|
||||
self:E( { "Modulation is invalid. Use DCS's enum radio.modulation.", Modulation } )
|
||||
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
|
||||
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
|
||||
IsValid = false
|
||||
end
|
||||
|
||||
|
||||
-- Check the Power
|
||||
if type( Power ) ~= "number" and IsValid then
|
||||
self:E( { "Power is invalid. ", Power } )
|
||||
if type(Power) ~= "number" and IsValid then
|
||||
self:E({"Power is invalid. ", Power})
|
||||
IsValid = false
|
||||
end
|
||||
Power = math.floor( math.abs( Power ) ) -- TODO: Find what is the maximum power allowed by DCS and limit power to that
|
||||
|
||||
Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
||||
|
||||
if IsValid then
|
||||
self:T2( { "Activating Beacon on ", Frequency, Modulation } )
|
||||
self:T2({"Activating Beacon on ", Frequency, Modulation})
|
||||
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
||||
trigger.action.radioTransmission( FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring( self.ID ) )
|
||||
|
||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
SCHEDULER:New( nil, function()
|
||||
self:StopRadioBeacon()
|
||||
end, {}, BeaconDuration )
|
||||
end
|
||||
end
|
||||
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
|
||||
|
||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
SCHEDULER:New( nil,
|
||||
function()
|
||||
self:StopRadioBeacon()
|
||||
end, {}, BeaconDuration)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Stops the AA TACAN BEACON
|
||||
--- Stop the Radio Beacon
|
||||
-- @param #BEACON self
|
||||
-- @return #BEACON self
|
||||
function BEACON:StopRadioBeacon()
|
||||
self:F()
|
||||
-- The unique name of the transmission is the class ID
|
||||
trigger.action.stopRadioTransmission( tostring( self.ID ) )
|
||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -406,26 +444,26 @@ end
|
||||
-- @param #BEACON self
|
||||
-- @param #number TACANChannel
|
||||
-- @param #string TACANMode
|
||||
-- @return #number Frequency
|
||||
-- @return #number Frequecy
|
||||
-- @return #nil if parameters are invalid
|
||||
function BEACON:_TACANToFrequency( TACANChannel, TACANMode )
|
||||
self:F3( { TACANChannel, TACANMode } )
|
||||
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
||||
self:F3({TACANChannel, TACANMode})
|
||||
|
||||
if type( TACANChannel ) ~= "number" then
|
||||
if type(TACANChannel) ~= "number" then
|
||||
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
||||
return nil -- error in arguments
|
||||
end
|
||||
end
|
||||
|
||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||
-- I have no idea what it does but it seems to work
|
||||
|
||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||
-- I have no idea what it does but it seems to work
|
||||
local A = 1151 -- 'X', channel >= 64
|
||||
local B = 64 -- channel >= 64
|
||||
|
||||
local B = 64 -- channel >= 64
|
||||
|
||||
if TACANChannel < 64 then
|
||||
B = 1
|
||||
end
|
||||
|
||||
|
||||
if TACANMode == 'Y' then
|
||||
A = 1025
|
||||
if TACANChannel < 64 then
|
||||
@@ -436,6 +474,6 @@ function BEACON:_TACANToFrequency( TACANChannel, TACANMode )
|
||||
A = 962
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return (A + TACANChannel - B) * 1000000
|
||||
end
|
||||
|
||||
@@ -241,16 +241,6 @@ function DATABASE:DeleteAirbase( AirbaseName )
|
||||
self.AIRBASES[AirbaseName] = nil
|
||||
end
|
||||
|
||||
--- Finds an AIRBASE based on the AirbaseName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName
|
||||
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
|
||||
function DATABASE:FindAirbase( AirbaseName )
|
||||
|
||||
local AirbaseFound = self.AIRBASES[AirbaseName]
|
||||
return AirbaseFound
|
||||
end
|
||||
|
||||
do -- Zones
|
||||
|
||||
--- Finds a @{Zone} based on the zone name.
|
||||
@@ -757,7 +747,9 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
|
||||
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 {}
|
||||
|
||||
@@ -765,7 +757,7 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
StaticTemplate.CoalitionID = CoalitionID
|
||||
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].UnitTemplate = StaticTemplate.units[1]
|
||||
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
|
||||
|
||||
@@ -131,8 +131,6 @@
|
||||
-- * Weapon data: Certain events populate weapon 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.
|
||||
-- -- EventData is an EVENTDATA structure.
|
||||
-- -- We use the EventData.IniUnit to smoke the tank Green.
|
||||
@@ -143,6 +141,7 @@
|
||||
-- EventData.IniUnit:SmokeGreen()
|
||||
-- end
|
||||
--
|
||||
--
|
||||
-- Find below an overview which events populate which information categories:
|
||||
--
|
||||
-- 
|
||||
@@ -152,7 +151,6 @@
|
||||
-- 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.
|
||||
-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory.
|
||||
--
|
||||
-- Example code snippet:
|
||||
--
|
||||
-- if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
@@ -174,6 +172,7 @@
|
||||
-- @module Core.Event
|
||||
-- @image Core_Event.JPG
|
||||
|
||||
|
||||
--- @type EVENT
|
||||
-- @field #EVENT.Events Events
|
||||
-- @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_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
|
||||
-- @type EVENTS
|
||||
@@ -227,13 +227,13 @@ EVENTS = {
|
||||
MarkChange = world.event.S_EVENT_MARK_CHANGE,
|
||||
MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
|
||||
-- Moose Events
|
||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
|
||||
-- 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
|
||||
@@ -304,6 +304,8 @@ EVENTS = {
|
||||
-- @field Core.ZONE#ZONE Zone The zone object.
|
||||
-- @field #string ZoneName The name of the zone.
|
||||
|
||||
|
||||
|
||||
local _EVENTMETA = {
|
||||
[world.event.S_EVENT_SHOT] = {
|
||||
Order = 1,
|
||||
@@ -560,6 +562,7 @@ local _EVENTMETA = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
--- The Events structure
|
||||
-- @type EVENT.Events
|
||||
-- @field #number IniUnit
|
||||
@@ -573,11 +576,12 @@ function EVENT:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
-- Add world event handler.
|
||||
self.EventHandler = world.addEventHandler( self )
|
||||
self.EventHandler = world.addEventHandler(self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Initializes the Events structure for the event.
|
||||
-- @param #EVENT self
|
||||
-- @param DCS#world.event EventID Event ID.
|
||||
@@ -591,7 +595,7 @@ function EVENT:Init( EventID, EventClass )
|
||||
self.Events[EventID] = {}
|
||||
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()
|
||||
|
||||
if not self.Events[EventID][EventPriority] then
|
||||
@@ -599,7 +603,7 @@ function EVENT:Init( EventID, EventClass )
|
||||
end
|
||||
|
||||
if not self.Events[EventID][EventPriority][EventClass] then
|
||||
self.Events[EventID][EventPriority][EventClass] = {}
|
||||
self.Events[EventID][EventPriority][EventClass] = {}
|
||||
end
|
||||
|
||||
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 DCS#world.event EventID Event ID.
|
||||
-- @return #EVENT self
|
||||
function EVENT:RemoveEvent( EventClass, EventID )
|
||||
function EVENT:RemoveEvent( EventClass, EventID )
|
||||
|
||||
-- Debug info.
|
||||
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 DCS#world.event EventID Event ID.
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Reset( EventObject ) -- R2.1
|
||||
function EVENT:Reset( EventObject ) --R2.1
|
||||
|
||||
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
||||
|
||||
@@ -653,11 +657,12 @@ function EVENT:Reset( EventObject ) -- R2.1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Base#BASE EventClass The self class object for which the events are removed.
|
||||
-- @return #EVENT self
|
||||
function EVENT:RemoveAll( EventClass )
|
||||
function EVENT:RemoveAll(EventClass)
|
||||
|
||||
local EventClassName = EventClass:GetClassNameAndID()
|
||||
|
||||
@@ -671,6 +676,8 @@ function EVENT:RemoveAll( EventClass )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
@@ -702,6 +709,7 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set a new listener for an `S_EVENT_X` event for a UNIT.
|
||||
-- @param #EVENT self
|
||||
-- @param #string UnitName The name of the UNIT.
|
||||
@@ -789,6 +797,7 @@ do -- OnDead
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- OnLand
|
||||
|
||||
--- Create an OnLand event handler for a group
|
||||
@@ -855,7 +864,7 @@ do -- Event Creation
|
||||
id = EVENTS.NewCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -870,7 +879,7 @@ do -- Event Creation
|
||||
id = EVENTS.DeleteCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -885,7 +894,7 @@ do -- Event Creation
|
||||
id = EVENTS.NewZone,
|
||||
time = timer.getTime(),
|
||||
zone = Zone,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -900,7 +909,7 @@ do -- Event Creation
|
||||
id = EVENTS.DeleteZone,
|
||||
time = timer.getTime(),
|
||||
zone = Zone,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -915,11 +924,12 @@ do -- Event Creation
|
||||
id = EVENTS.NewZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a ZoneGoal Deletion Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
||||
@@ -930,11 +940,12 @@ do -- Event Creation
|
||||
id = EVENTS.DeleteZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit.
|
||||
@@ -944,8 +955,8 @@ do -- Event Creation
|
||||
local Event = {
|
||||
id = EVENTS.PlayerEnterUnit,
|
||||
time = timer.getTime(),
|
||||
initiator = PlayerUnit:GetDCSObject(),
|
||||
}
|
||||
initiator = PlayerUnit:GetDCSObject()
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -959,8 +970,8 @@ do -- Event Creation
|
||||
local Event = {
|
||||
id = EVENTS.PlayerEnterAircraft,
|
||||
time = timer.getTime(),
|
||||
initiator = PlayerUnit:GetDCSObject(),
|
||||
}
|
||||
initiator = PlayerUnit:GetDCSObject()
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -972,24 +983,25 @@ end
|
||||
-- @param #EVENTDATA Event Event data table.
|
||||
function EVENT:onEvent( Event )
|
||||
|
||||
--- Function to handle errors.
|
||||
local ErrorHandler = function( errmsg )
|
||||
|
||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( debug.traceback() )
|
||||
end
|
||||
|
||||
return errmsg
|
||||
end
|
||||
|
||||
|
||||
-- Get event meta data.
|
||||
local EventMeta = _EVENTMETA[Event.id]
|
||||
|
||||
-- Check if this is a known event?
|
||||
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
|
||||
self.MissionEnd = true
|
||||
end
|
||||
@@ -997,50 +1009,27 @@ function EVENT:onEvent( Event )
|
||||
if Event.initiator then
|
||||
|
||||
Event.IniObjectCategory = Event.initiator:getCategory()
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
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 = ""
|
||||
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
|
||||
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||
---
|
||||
-- Static
|
||||
---
|
||||
if Event.id==31 then
|
||||
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
local ID = Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( ID ) )
|
||||
local ID=Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniCoalition = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniTypeName = "Ejected Pilot"
|
||||
elseif Event.id == 33 then -- ejection seat discarded
|
||||
elseif Event.id == 33 then -- ejection seat discarded
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
local ID = Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format( "Ejection Seat ID %s", tostring( ID ) )
|
||||
local ID=Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format("Ejection Seat ID %s", tostring(ID))
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniCoalition = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniTypeName = "Ejection Seat"
|
||||
else
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
@@ -1051,9 +1040,47 @@ function EVENT:onEvent( Event )
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
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
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.CARGO then
|
||||
---
|
||||
-- Cargo
|
||||
---
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
@@ -1064,19 +1091,26 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.SCENERY then
|
||||
---
|
||||
-- Scenery
|
||||
---
|
||||
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
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
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.BASE then
|
||||
---
|
||||
-- Base Object
|
||||
---
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = AIRBASE:FindByName( Event.IniDCSUnitName )
|
||||
Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
@@ -1084,7 +1118,12 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
|
||||
if Event.target then
|
||||
|
||||
---
|
||||
-- TARGET
|
||||
---
|
||||
|
||||
-- Target category.
|
||||
Event.TgtObjectCategory = Event.target:getCategory()
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||||
@@ -1097,9 +1136,7 @@ function EVENT:onEvent( Event )
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
-- if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
-- end
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
@@ -1118,18 +1155,18 @@ function EVENT:onEvent( Event )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
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.TgtUnit = nil
|
||||
Event.TgtCoalition = 0
|
||||
Event.TgtCategory = 0
|
||||
if Event.id == 6 then
|
||||
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
|
||||
elseif Event.id == 33 then
|
||||
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
|
||||
else
|
||||
Event.TgtTypeName = "Static"
|
||||
@@ -1147,6 +1184,7 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
end
|
||||
|
||||
-- Weapon.
|
||||
if Event.weapon then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.Weapon:getTypeName()
|
||||
@@ -1155,49 +1193,48 @@ function EVENT:onEvent( Event )
|
||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||
-- Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
|
||||
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
|
||||
if Event.place then
|
||||
if Event.id == EVENTS.LandingAfterEjection then
|
||||
if Event.id==EVENTS.LandingAfterEjection then
|
||||
-- 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.
|
||||
-- Event.Place=UNIT:Find(Event.place)
|
||||
--Event.Place=UNIT:Find(Event.place)
|
||||
else
|
||||
Event.Place = AIRBASE:Find( Event.place )
|
||||
Event.PlaceName = Event.Place:GetName()
|
||||
Event.Place=AIRBASE:Find(Event.place)
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
end
|
||||
end
|
||||
|
||||
-- Mark points.
|
||||
if Event.idx then
|
||||
Event.MarkID = Event.idx
|
||||
Event.MarkVec3 = Event.pos
|
||||
Event.MarkCoordinate = COORDINATE:NewFromVec3( Event.pos )
|
||||
Event.MarkText = Event.text
|
||||
Event.MarkCoalition = Event.coalition
|
||||
Event.MarkID=Event.idx
|
||||
Event.MarkVec3=Event.pos
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
Event.MarkText=Event.text
|
||||
Event.MarkCoalition=Event.coalition
|
||||
Event.MarkGroupID = Event.groupID
|
||||
end
|
||||
|
||||
-- Cargo object.
|
||||
if Event.cargo then
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
-- Zone object.
|
||||
if Event.zone then
|
||||
Event.Zone = Event.zone
|
||||
Event.ZoneName = Event.zone.ZoneName
|
||||
end
|
||||
|
||||
-- Priority order.
|
||||
local PriorityOrder = EventMeta.Order
|
||||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||||
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
|
||||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||||
|
||||
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.
|
||||
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() } )
|
||||
-- end
|
||||
--end
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
Event.IniGroup = Event.IniGroup or GROUP:FindByName( Event.IniDCSGroupName )
|
||||
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 EventData.EventUnit then
|
||||
@@ -1221,39 +1258,35 @@ function EVENT:onEvent( Event )
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash 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()
|
||||
|
||||
if (EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName) or
|
||||
(EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName) then
|
||||
|
||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall( function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- 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
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall( function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -1271,48 +1304,43 @@ function EVENT:onEvent( Event )
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash 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.
|
||||
local GroupName = EventClass:GetName()
|
||||
|
||||
if (EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName) or
|
||||
(EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName) then
|
||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall( function()
|
||||
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
|
||||
|
||||
-- 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
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall( function()
|
||||
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
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
-- self:RemoveEvent( EventClass, Event.id )
|
||||
--self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
|
||||
-- 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.
|
||||
if not EventData.EventUnit then
|
||||
@@ -1321,28 +1349,25 @@ function EVENT:onEvent( Event )
|
||||
if EventData.EventFunction then
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall( function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
else
|
||||
|
||||
-- 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
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall( function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
end, ErrorHandler )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1365,7 +1390,7 @@ function EVENT:onEvent( Event )
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
end
|
||||
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
|
||||
|
||||
Event = nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,8 @@
|
||||
-- * Send message to all players.
|
||||
-- * Send messages to a coalition.
|
||||
-- * 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 @{Wrapper.Group} using @{#MESSAGE.ToGroup}()
|
||||
-- * To a @{Wrapper.Unit} using @{#MESSAGE.ToUnit}()
|
||||
-- * To a coalition using @{#MESSAGE.ToCoalition}().
|
||||
-- * To the red coalition using @{#MESSAGE.ToRed}().
|
||||
-- * To the blue coalition using @{#MESSAGE.ToBlue}().
|
||||
@@ -194,18 +197,21 @@ function MESSAGE:ToClient( Client, Settings )
|
||||
if Client and Client:GetClientGroupID() 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.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
|
||||
local Unit = Client:GetClient()
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||
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.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -232,6 +238,31 @@ function MESSAGE:ToGroup( Group, Settings )
|
||||
|
||||
return self
|
||||
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.
|
||||
-- @param #MESSAGE self
|
||||
-- @return #MESSAGE
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler)
|
||||
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
|
||||
--
|
||||
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
|
||||
--
|
||||
|
||||
@@ -201,7 +201,7 @@ do -- SET_BASE
|
||||
self:F2( { ObjectName = ObjectName } )
|
||||
|
||||
local TriggerEvent = true
|
||||
if NoTriggerEvent == false then
|
||||
if NoTriggerEvent then
|
||||
TriggerEvent = false
|
||||
end
|
||||
|
||||
@@ -317,7 +317,7 @@ do -- SET_BASE
|
||||
|
||||
for _, Object in pairs( union.Set ) do
|
||||
if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then
|
||||
intersection:AddObject( intersection )
|
||||
intersection:AddObject( Object )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1004,7 +1004,7 @@ do -- SET_GROUP
|
||||
|
||||
--- Gets the Set.
|
||||
-- @param #SET_GROUP self
|
||||
-- @return #SET_GROUP self
|
||||
-- @return #table Table of objects
|
||||
function SET_GROUP:GetAliveSet()
|
||||
self:F2()
|
||||
|
||||
@@ -1064,12 +1064,17 @@ do -- SET_GROUP
|
||||
function SET_GROUP:AddGroup( group )
|
||||
|
||||
self:Add( group:GetName(), group )
|
||||
|
||||
-- I set the default cargo bay weight limit each time a new group is added to the set.
|
||||
for UnitID, UnitData in pairs( group:GetUnits() ) do
|
||||
UnitData:SetCargoBayWeightLimit()
|
||||
|
||||
if not DontSetCargoBayLimit then
|
||||
-- I set the default cargo bay weight limit each time a new group is added to the set.
|
||||
-- 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
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1122,8 +1127,10 @@ do -- SET_GROUP
|
||||
|
||||
local NearestGroup = nil -- Wrapper.Group#GROUP
|
||||
local ClosestDistance = nil
|
||||
|
||||
for ObjectID, ObjectData in pairs( self.Set ) do
|
||||
|
||||
local Set = self:GetAliveSet()
|
||||
|
||||
for ObjectID, ObjectData in pairs( Set ) do
|
||||
if NearestGroup == nil then
|
||||
NearestGroup = ObjectData
|
||||
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
@@ -1327,7 +1334,11 @@ do -- SET_GROUP
|
||||
if Event.IniDCSUnit then
|
||||
local ObjectName, Object = self:FindInDatabase( Event )
|
||||
if ObjectName then
|
||||
if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed.
|
||||
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 )
|
||||
end
|
||||
end
|
||||
@@ -5618,7 +5629,7 @@ do -- SET_ZONE
|
||||
if self.Filter.Prefixes then
|
||||
local MZonePrefix = false
|
||||
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
|
||||
self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
|
||||
self:T2( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
|
||||
if string.find( MZoneName, ZonePrefix, 1 ) then
|
||||
MZonePrefix = true
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -2827,21 +2827,40 @@ end
|
||||
-- The method will search for a #-mark, and will return the text before the #-mark.
|
||||
-- It will return nil of no prefix was found.
|
||||
-- @param #SPAWN self
|
||||
-- @param DCS#UNIT DCSUnit The @{DCSUnit} to be searched.
|
||||
-- @return #string The prefix
|
||||
-- @return #nil Nothing found
|
||||
-- @param Wrapper.Group#GROUP SpawnGroup The GROUP object.
|
||||
-- @return #string The prefix or #nil if nothing was found.
|
||||
function SPAWN:_GetPrefixFromGroup( SpawnGroup )
|
||||
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
|
||||
|
||||
local GroupName = SpawnGroup:GetName()
|
||||
|
||||
if GroupName then
|
||||
local SpawnPrefix = string.match( GroupName, ".*#" )
|
||||
if SpawnPrefix then
|
||||
SpawnPrefix = SpawnPrefix:sub( 1, -2 )
|
||||
end
|
||||
|
||||
local SpawnPrefix=self:_GetPrefixFromGroupName(GroupName)
|
||||
|
||||
return SpawnPrefix
|
||||
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
|
||||
end
|
||||
|
||||
@@ -3235,24 +3254,27 @@ function SPAWN:_OnBirth( EventData )
|
||||
|
||||
end
|
||||
|
||||
--- Obscolete
|
||||
-- @todo Need to delete this... _DATABASE does this now ...
|
||||
|
||||
--- @param #SPAWN self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SPAWN:_OnDeadOrCrash( EventData )
|
||||
self:F( self.SpawnTemplatePrefix )
|
||||
|
||||
local SpawnGroup = EventData.IniGroup
|
||||
|
||||
if SpawnGroup then
|
||||
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
|
||||
|
||||
local unit=UNIT:FindByName(EventData.IniUnitName)
|
||||
|
||||
if unit then
|
||||
|
||||
local EventPrefix = self:_GetPrefixFromGroupName(unit.GroupName)
|
||||
|
||||
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
|
||||
self:T( { "Dead event: " .. EventPrefix } )
|
||||
if EventPrefix == self.SpawnTemplatePrefix or (self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix) then
|
||||
self.AliveUnits = self.AliveUnits - 1
|
||||
self:T( "Alive Units: " .. self.AliveUnits )
|
||||
|
||||
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
|
||||
|
||||
self.AliveUnits = self.AliveUnits - 1
|
||||
|
||||
self:T( "Alive Units: " .. self.AliveUnits )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,14 +46,14 @@
|
||||
-- @field #number InitOffsetAngle Link offset angle in degrees.
|
||||
-- @field #number InitStaticHeading Heading of the static.
|
||||
-- @field #string InitStaticLivery Livery for aircraft.
|
||||
-- @field #string InitStaticShape Shape of teh static.
|
||||
-- @field #string InitStaticShape Shape of the static.
|
||||
-- @field #string InitStaticType Type of the static.
|
||||
-- @field #string InitStaticCategory Categrory of the static.
|
||||
-- @field #string InitStaticName Name of the static.
|
||||
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
|
||||
-- @field #boolean InitDead Set static to be dead if true.
|
||||
-- @field #boolean InitCargo If true, static can act as cargo.
|
||||
-- @field #number InitCargoMass Mass of cargo in kg.
|
||||
-- @field #boolean InitStaticDead Set static to be dead if true.
|
||||
-- @field #boolean InitStaticCargo If true, static can act as cargo.
|
||||
-- @field #number InitStaticCargoMass Mass of cargo in kg.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ end
|
||||
-- @param #number Mass Mass of the cargo in kg.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitCargoMass(Mass)
|
||||
self.InitCargoMass=Mass
|
||||
self.InitStaticCargoMass=Mass
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -269,7 +269,16 @@ end
|
||||
-- @param #boolean IsCargo If true, this static can act as cargo.
|
||||
-- @return #SPAWNSTATIC self
|
||||
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
|
||||
end
|
||||
|
||||
@@ -417,16 +426,16 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
Template.livery_id=self.InitStaticLivery
|
||||
end
|
||||
|
||||
if self.InitDead~=nil then
|
||||
Template.dead=self.InitDead
|
||||
if self.InitStaticDead~=nil then
|
||||
Template.dead=self.InitStaticDead
|
||||
end
|
||||
|
||||
if self.InitCargo~=nil then
|
||||
Template.canCargo=self.InitCargo
|
||||
if self.InitStaticCargo~=nil then
|
||||
Template.canCargo=self.InitStaticCargo
|
||||
end
|
||||
|
||||
if self.InitCargoMass~=nil then
|
||||
Template.mass=self.InitCargoMass
|
||||
if self.InitStaticCargoMass~=nil then
|
||||
Template.mass=self.InitStaticCargoMass
|
||||
end
|
||||
|
||||
if self.InitLinkUnit then
|
||||
@@ -479,6 +488,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
-- ED's dirty way to spawn FARPS.
|
||||
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
||||
else
|
||||
self:T("Spawning Static")
|
||||
self:T2({Template=Template})
|
||||
Static=coalition.addStaticObject(CountryID, Template)
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -487,8 +487,10 @@ do -- Object
|
||||
-- @field UNIT
|
||||
-- @field WEAPON
|
||||
-- @field STATIC
|
||||
-- @field SCENERY
|
||||
-- @field BASE
|
||||
-- @field SCENERY
|
||||
-- @field CARGO
|
||||
|
||||
|
||||
--- @type Object.Desc
|
||||
-- @extends #Desc
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -949,11 +949,14 @@ function PSEUDOATC:LocalAirports(GID, UID)
|
||||
for _,airbase in pairs(airports) do
|
||||
|
||||
local name=airbase:getName()
|
||||
local q=AIRBASE:FindByName(name):GetCoordinate()
|
||||
local d=q:Get2DDistance(pos)
|
||||
local a=AIRBASE:FindByName(name)
|
||||
if a then
|
||||
local q=a:GetCoordinate()
|
||||
local d=q:Get2DDistance(pos)
|
||||
|
||||
-- Add to table.
|
||||
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
|
||||
-- Add to table.
|
||||
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1289,7 +1289,7 @@ end
|
||||
|
||||
--- Include all airports which lie in a zone as possible destinations.
|
||||
-- @param #RAT self
|
||||
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
|
||||
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetDestinationsFromZone(zone)
|
||||
self:F2(zone)
|
||||
@@ -1305,7 +1305,7 @@ end
|
||||
|
||||
--- Include all airports which lie in a zone as possible destinations.
|
||||
-- @param #RAT self
|
||||
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
|
||||
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetDeparturesFromZone(zone)
|
||||
self:F2(zone)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1650,7 +1650,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
|
||||
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
|
||||
|
||||
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",
|
||||
PlayerName,
|
||||
@@ -1705,7 +1705,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
|
||||
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
|
||||
|
||||
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 )",
|
||||
PlayerName,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
--
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: Nov 2021
|
||||
-- Last Update: Feb 2022
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -59,6 +59,7 @@ SEAD = {
|
||||
Padding = 10,
|
||||
CallBack = nil,
|
||||
UseCallBack = false,
|
||||
debug = false,
|
||||
}
|
||||
|
||||
--- Missile enumerators
|
||||
@@ -76,6 +77,8 @@ SEAD = {
|
||||
["X_25"] = "X_25",
|
||||
["X_31"] = "X_31",
|
||||
["Kh25"] = "Kh25",
|
||||
["BGM_109"] = "BGM_109",
|
||||
["AGM_154"] = "AGM_154",
|
||||
}
|
||||
|
||||
--- Missile enumerators - from DCS ME and Wikipedia
|
||||
@@ -85,7 +88,7 @@ SEAD = {
|
||||
["AGM_88"] = { 150, 3},
|
||||
["AGM_45"] = { 12, 2},
|
||||
["AGM_122"] = { 16.5, 2.3},
|
||||
["AGM_84"] = { 280, 0.85},
|
||||
["AGM_84"] = { 280, 0.8},
|
||||
["ALARM"] = { 45, 2},
|
||||
["LD-10"] = { 60, 4},
|
||||
["X_58"] = { 70, 4},
|
||||
@@ -93,6 +96,8 @@ SEAD = {
|
||||
["X_25"] = { 25, 0.76},
|
||||
["X_31"] = {150, 3},
|
||||
["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.
|
||||
@@ -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' } )
|
||||
function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
local self = BASE:Inherit( self, FSM:New() )
|
||||
self:T( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
@@ -122,14 +127,21 @@ function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
local padding = Padding or 10
|
||||
if padding < 10 then padding = 10 end
|
||||
self.Padding = padding
|
||||
self.UseEmissionsOnOff = false
|
||||
self.UseEmissionsOnOff = true
|
||||
|
||||
self.debug = false
|
||||
|
||||
self.CallBack = nil
|
||||
self.UseCallBack = false
|
||||
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@@ -213,7 +225,7 @@ function SEAD:_CheckHarms(WeaponName)
|
||||
local hit = false
|
||||
local name = ""
|
||||
for _,_name in pairs (SEAD.Harms) do
|
||||
if string.find(WeaponName,_name,1) then
|
||||
if string.find(WeaponName,_name,1,true) then
|
||||
hit = true
|
||||
name = _name
|
||||
break
|
||||
@@ -249,6 +261,186 @@ function SEAD:_GetDistance(_point1, _point2)
|
||||
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.
|
||||
-- @param #SEAD self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
@@ -256,6 +448,7 @@ end
|
||||
function SEAD:HandleEventShot( EventData )
|
||||
self:T( { EventData.id } )
|
||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
|
||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
@@ -270,114 +463,55 @@ function SEAD:HandleEventShot( EventData )
|
||||
local _targetskill = "Random"
|
||||
local _targetgroupname = "none"
|
||||
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
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
_targetgroup = _targetUnit:GetGroup()
|
||||
_targetgroupname = _targetgroup:GetName() -- group name
|
||||
local _targetUnitName = _targetUnit:GetName()
|
||||
_targetUnit:GetSkill()
|
||||
_targetskill = _targetUnit:GetSkill()
|
||||
self:T(string.format("*** Targetcat = %d",targetcat))
|
||||
if targetcat == Object.Category.UNIT then -- UNIT
|
||||
self:T("*** Target Category UNIT")
|
||||
_targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
_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
|
||||
-- see if we are shot at
|
||||
local SEADGroupFound = false
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||
self:T( _targetgroupname, SEADGroupPrefix )
|
||||
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
|
||||
self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
|
||||
if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
|
||||
SEADGroupFound = true
|
||||
self:T( '*** SEAD - Group Match Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
if SEADGroupFound == true then -- yes we are being attacked
|
||||
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) -- 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
|
||||
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
|
||||
end
|
||||
end
|
||||
return self
|
||||
|
||||
@@ -4,6 +4,7 @@ __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Templates.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/Beacon.lua' )
|
||||
|
||||
@@ -1740,6 +1740,10 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
end
|
||||
|
||||
-- Wind
|
||||
-- Adding a space after each digit of WINDFROM to convert this to aviation-speak for TTS via SRS
|
||||
if self.useSRS then
|
||||
WINDFROM = string.gsub(WINDFROM,".", "%1 ")
|
||||
end
|
||||
if self.metric then
|
||||
subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED )
|
||||
else
|
||||
@@ -2197,7 +2201,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
|
||||
-- TACAN
|
||||
if self.tacan then
|
||||
subtitle = string.format( "TACAN channel %dX", self.tacan )
|
||||
subtitle = string.format( "TACAN channel %dX Ray", self.tacan )
|
||||
if not self.useSRS then
|
||||
self:Transmission( ATIS.Sound.TACANChannel, 1.0, subtitle )
|
||||
self.radioqueue:Number2Transmission( tostring( self.tacan ), nil, 0.2 )
|
||||
|
||||
@@ -32,9 +32,12 @@
|
||||
-- * [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 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**]
|
||||
-- * [HMS Invincible](https://en.wikipedia.org/wiki/HMS_Invincible_(R05) (R05) [**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**]
|
||||
-- * [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:**
|
||||
--
|
||||
@@ -51,9 +54,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.
|
||||
--
|
||||
-- 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, HMS Invincible, 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.
|
||||
-- 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.
|
||||
--
|
||||
@@ -113,10 +116,11 @@
|
||||
-- * [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)
|
||||
-- * [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**
|
||||
-- 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.
|
||||
@@ -1260,7 +1264,7 @@ AIRBOSS = {
|
||||
|
||||
--- Aircraft types capable of landing on carrier (human+AI).
|
||||
-- @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, HMS Invincible, USS Tarawa, USS America, and Juan Carlos I.
|
||||
-- @field #string A4EC A-4E Community mod.
|
||||
-- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics.
|
||||
-- @field #string F14A F-14A by Heatblur.
|
||||
@@ -1296,6 +1300,8 @@ AIRBOSS.AircraftCarrier={
|
||||
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
|
||||
-- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module]
|
||||
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
|
||||
-- @field #string HERMES HMS Hermes (R12) [V/STOL Carrier]
|
||||
-- @field #string INVINCIBLE HMS Invincible (R05) [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 JCARLOS Juan Carlos I (L61) [V/STOL Carrier]
|
||||
@@ -1309,6 +1315,8 @@ AIRBOSS.CarrierType = {
|
||||
STENNIS = "Stennis",
|
||||
FORRESTAL = "Forrestal",
|
||||
VINSON = "VINSON",
|
||||
HERMES = "HERMES81",
|
||||
INVINCIBLE = "hms_invincible",
|
||||
TARAWA = "LHA_Tarawa",
|
||||
AMERICA = "USS America LHA-6",
|
||||
JCARLOS = "L61",
|
||||
@@ -1980,6 +1988,12 @@ function AIRBOSS:New( carriername, alias )
|
||||
elseif self.carriertype == AIRBOSS.CarrierType.VINSON then
|
||||
-- TODO: Carl Vinson parameters.
|
||||
self:_InitStennis()
|
||||
elseif self.carriertype == AIRBOSS.CarrierType.HERMES then
|
||||
-- Hermes parameters.
|
||||
self:_InitHermes()
|
||||
elseif self.carriertype == AIRBOSS.CarrierType.INVINCIBLE then
|
||||
-- Invincible parameters.
|
||||
self:_InitInvincible()
|
||||
elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then
|
||||
-- Tarawa parameters.
|
||||
self:_InitTarawa()
|
||||
@@ -2082,7 +2096,7 @@ function AIRBOSS:New( carriername, alias )
|
||||
-- cL:FlareYellow()
|
||||
|
||||
-- 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.INVINCIBLE or 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.
|
||||
local w1 = stern:Translate( self.carrierparam.wire1, FB, true )
|
||||
@@ -2811,13 +2825,29 @@ end
|
||||
-- @param #number Low
|
||||
-- @param #number LOW
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:SetGlideslopeErrorThresholds( _max, _min, High, HIGH, Low, LOW )
|
||||
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
|
||||
|
||||
function AIRBOSS:SetGlideslopeErrorThresholds(_max,_min, High, HIGH, Low, LOW)
|
||||
|
||||
--Check if V/STOL Carrier
|
||||
if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or 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
|
||||
|
||||
-- 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
|
||||
end
|
||||
|
||||
@@ -2832,15 +2862,33 @@ end
|
||||
-- @param #number RightMed
|
||||
-- @param #number RIGHT
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:SetLineupErrorThresholds( _max, _min, Left, LeftMed, LEFT, Right, RightMed, RIGHT )
|
||||
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
|
||||
|
||||
function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LeftMed, LEFT, Right, RightMed, RIGHT)
|
||||
|
||||
--Check if V/STOL Carrier -- Pene testing
|
||||
if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or 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
|
||||
|
||||
-- V/STOL Values -- allow a larger LUE for V/STOL operations
|
||||
self.lue._max=_max or 1.8
|
||||
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
|
||||
end
|
||||
|
||||
@@ -4384,6 +4432,86 @@ function AIRBOSS:_InitForrestal()
|
||||
|
||||
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( 1.6 ) -- Not more than 1.6 NM port.
|
||||
self.BreakLate.Zmax = UTILS.NMToMeters( 1 ) -- Not more than 1 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 R05 HMS Invincible carrier.
|
||||
-- @param #AIRBOSS self
|
||||
function AIRBOSS:_InitInvincible()
|
||||
|
||||
-- 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( 1.6 ) -- Not more than 1.6 NM port.
|
||||
self.BreakLate.Zmax = UTILS.NMToMeters( 1 ) -- Not more than 1 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.
|
||||
-- @param #AIRBOSS self
|
||||
function AIRBOSS:_InitTarawa()
|
||||
@@ -5007,14 +5135,16 @@ function AIRBOSS:_GetAircraftAoA( playerData )
|
||||
aoa.Fast = 8.25 -- =17.5/2
|
||||
aoa.FAST = 8.00 -- =16.5/2
|
||||
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
|
||||
aoa.Slow = 13.0
|
||||
aoa.OnSpeedMax = 12.0
|
||||
aoa.OnSpeed = 11.0
|
||||
aoa.OnSpeedMin = 10.0
|
||||
aoa.Fast = 8.0
|
||||
aoa.FAST = 7.5
|
||||
|
||||
-- AV-8B Harrier parameters. Tuning done on the Fast AoA to allow for abeam and ninety at Nozzles 55. Pene testing
|
||||
aoa.SLOW = 16.0
|
||||
aoa.Slow = 13.5
|
||||
aoa.OnSpeedMax = 12.5
|
||||
aoa.OnSpeed = 10.0
|
||||
aoa.OnSpeedMin = 9.5
|
||||
aoa.Fast = 8.0
|
||||
aoa.FAST = 7.5
|
||||
|
||||
end
|
||||
|
||||
return aoa
|
||||
@@ -5273,8 +5403,8 @@ function AIRBOSS:_GetAircraftParameters( playerData, step )
|
||||
elseif skyhawk then
|
||||
alt = UTILS.FeetToMeters( 300 ) -- ?
|
||||
elseif harrier then
|
||||
-- 300-325 ft
|
||||
alt = UTILS.FeetToMeters( 300 ) -- Need to verify
|
||||
alt=UTILS.FeetToMeters(312)-- 300-325 ft
|
||||
|
||||
end
|
||||
|
||||
aoa = aoaac.OnSpeed
|
||||
@@ -6209,7 +6339,7 @@ function AIRBOSS:_GetMarshalAltitude( stack, case )
|
||||
p2 = Carrier:Translate( UTILS.NMToMeters( 1.5 ), hdg )
|
||||
|
||||
-- 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.INVINCIBLE or 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.
|
||||
p1 = Carrier:Translate( UTILS.NMToMeters( 1.0 ), hdg + 90 )
|
||||
@@ -8048,7 +8178,7 @@ function AIRBOSS:OnEventLand( EventData )
|
||||
self:T( self.lid .. text )
|
||||
|
||||
-- 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.INVINCIBLE or 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".
|
||||
self:RadioTransmission( self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true )
|
||||
@@ -8083,7 +8213,7 @@ function AIRBOSS:OnEventLand( EventData )
|
||||
-- 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.INVINCIBLE or 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
|
||||
local coord = EventData.IniUnit:GetCoordinate()
|
||||
@@ -9121,7 +9251,7 @@ function AIRBOSS:_CheckForLongDownwind( playerData )
|
||||
local limit = UTILS.NMToMeters( -1.6 )
|
||||
|
||||
-- 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.INVINCIBLE or 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 )
|
||||
end
|
||||
|
||||
@@ -9208,7 +9338,7 @@ function AIRBOSS:_Ninety( playerData )
|
||||
self:_PlayerHint( playerData )
|
||||
|
||||
-- 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.INVINCIBLE or 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.
|
||||
self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.FINAL )
|
||||
else
|
||||
@@ -9464,8 +9594,9 @@ function AIRBOSS:_Groove( playerData )
|
||||
-- Speed difference.
|
||||
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.
|
||||
if playerData.unit:IsInZone( ZoneALS ) and stable then
|
||||
@@ -9497,8 +9628,8 @@ function AIRBOSS:_Groove( playerData )
|
||||
-- Speed difference.
|
||||
local dv = math.abs( vplayer - vcarrier )
|
||||
|
||||
-- Stable when v<10 km/h.
|
||||
local stable = dv < 10
|
||||
-- Stable when v<15 km/h.
|
||||
local stable=dv<15
|
||||
|
||||
-- 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
|
||||
@@ -9537,25 +9668,26 @@ function AIRBOSS:_Groove( playerData )
|
||||
-- Nothing else necessary.
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP)
|
||||
|
||||
-- if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then
|
||||
-- Get groove time
|
||||
-- local vSlow=groovedata.time
|
||||
-- If too slow wave off.
|
||||
-- if vSlow >75 then
|
||||
|
||||
-- LSO Wave off!
|
||||
-- self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true)
|
||||
-- playerData.Tlso=timer.getTime()
|
||||
|
||||
-- Player was waved Off
|
||||
-- playerData.waveoff=true
|
||||
-- return
|
||||
-- end
|
||||
-- end
|
||||
end
|
||||
|
||||
-- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP)--- Need to think more about this.
|
||||
|
||||
--if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then
|
||||
-- Get groove time
|
||||
--local vSlow=groovedata.time
|
||||
-- If too slow wave off.
|
||||
--if vSlow >75 then
|
||||
|
||||
-- LSO Wave off!
|
||||
--self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true)
|
||||
--playerData.Tlso=timer.getTime()
|
||||
|
||||
-- Player was waved Off
|
||||
--playerData.waveoff=true
|
||||
--return
|
||||
--end
|
||||
--end
|
||||
|
||||
-- Groovedata step.
|
||||
groovedata.Step = playerData.step
|
||||
@@ -9726,8 +9858,8 @@ function AIRBOSS:_CheckWaveOff( glideslopeError, lineupError, AoA, playerData )
|
||||
-- For the harrier, we allow a bit more room.
|
||||
if playerData.actype == AIRBOSS.AircraftCarrier.AV8B then
|
||||
glMax = 2.6
|
||||
glMin = -2.0
|
||||
luAbs = 4.1 -- Testing Pene (WIP) needs feedback to tighten up tolerences.
|
||||
glMin = -2.2 -- Testing, @Engines may be just dragging it in on Hermes, or the carrier parameters need adjusting.
|
||||
luAbs = 4.1 -- Testing Pene.
|
||||
|
||||
end
|
||||
|
||||
@@ -9893,17 +10025,23 @@ function AIRBOSS:_GetSternCoord()
|
||||
local hdg = self.carrier:GetHeading()
|
||||
|
||||
-- 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.
|
||||
self.sterncoord:UpdateFromCoordinate( self:GetCoordinate() )
|
||||
-- local stern=self:GetCoordinate()
|
||||
|
||||
-- Stern coordinate (sterndist<0).
|
||||
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
|
||||
-- Tarawa: Translate 8 meters port.
|
||||
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 8, FB - 90, true, true )
|
||||
elseif self.carriertype == AIRBOSS.CarrierType.STENNIS then
|
||||
-- Stern coordinate (sterndist<0). --Pene testing Case III
|
||||
if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or 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
|
||||
-- CASE III V/STOL translation Due over deck approach if needed.
|
||||
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.
|
||||
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true )
|
||||
elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then
|
||||
@@ -10536,7 +10674,8 @@ function AIRBOSS:_GetZoneRunwayBox()
|
||||
return self.zoneRunwaybox
|
||||
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, HMS Invincible, 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
|
||||
-- @param #AIRBOSS self
|
||||
-- @return Core.Zone#ZONE_POLYGON Zone surrounding landing runway.
|
||||
@@ -10548,9 +10687,9 @@ function AIRBOSS:_GetZoneAbeamLandingSpot()
|
||||
-- Current carrier heading.
|
||||
local FB = self:GetFinalBearing( false )
|
||||
|
||||
-- Coordinate array.
|
||||
local p = {}
|
||||
|
||||
-- Coordinate array. Pene Testing extended Abeam landing spot V/STOL.
|
||||
local p={}
|
||||
|
||||
-- Points.
|
||||
p[1] = S:Translate( 15, FB ):Translate( 15, FB + 90 ) -- Top-Right
|
||||
p[2] = S:Translate( -45, FB ):Translate( 15, FB + 90 ) -- Bottom-Right
|
||||
@@ -10640,7 +10779,7 @@ function AIRBOSS:_GetZoneHolding( case, stack )
|
||||
self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", Post:GetVec2(), self.marshalradius )
|
||||
|
||||
-- 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.INVINCIBLE or 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 ) )
|
||||
end
|
||||
|
||||
@@ -10692,7 +10831,7 @@ function AIRBOSS:_GetZoneCommence( case, stack )
|
||||
-- Three position
|
||||
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.INVINCIBLE or 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 )
|
||||
|
||||
@@ -10983,7 +11122,7 @@ function AIRBOSS:_GetAltCarrier( unit )
|
||||
return h
|
||||
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, HMS Invincible, and HMS Hermes and Invincible 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
|
||||
-- @return Core.Point#COORDINATE Optimal landing coordinate.
|
||||
function AIRBOSS:_GetOptLandingCoordinate()
|
||||
@@ -10993,45 +11132,28 @@ function AIRBOSS:_GetOptLandingCoordinate()
|
||||
|
||||
-- Stern coordinate.
|
||||
-- local stern=self:_GetSternCoord()
|
||||
|
||||
-- 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.INVINCIBLE or 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.
|
||||
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.AMERICA then
|
||||
|
||||
-- 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 ) )
|
||||
self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-90, true, true)
|
||||
--stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
|
||||
|
||||
-- Atlitude 120 ft.
|
||||
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Ideally we want to land between 2nd and 3rd wire.
|
||||
@@ -11059,7 +11181,21 @@ function AIRBOSS:_GetLandingSpotCoordinate()
|
||||
-- Stern coordinate.
|
||||
-- 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.INVINCIBLE then
|
||||
|
||||
-- Using spot 3 as the default
|
||||
local hdg = self:GetHeading()
|
||||
|
||||
self.landingspotcoord:Translate( 69, hdg, true, true ):SetAltitude( self.carrierparam.deckheight )
|
||||
-- This location looks good.
|
||||
elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then
|
||||
|
||||
-- Landing 100 ft abeam, 120 alt.
|
||||
local hdg = self:GetHeading()
|
||||
@@ -11127,7 +11263,7 @@ end
|
||||
|
||||
--- Get wind direction and speed at carrier position.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number alt Altitude ASL in meters. Default 50 m.
|
||||
-- @param #number alt Altitude ASL in meters. Default 15 m.
|
||||
-- @param #boolean magnetic Direction including magnetic declination.
|
||||
-- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position.
|
||||
-- @return #number Direction the wind is blowing **from** in degrees.
|
||||
@@ -11137,8 +11273,8 @@ function AIRBOSS:GetWind( alt, magnetic, coord )
|
||||
-- Current position of the carrier or input.
|
||||
local cv = coord or self:GetCoordinate()
|
||||
|
||||
-- Wind direction and speed. By default at 50 meters ASL.
|
||||
local Wdir, Wspeed = cv:GetWind( alt or 50 )
|
||||
-- Wind direction and speed. By default at 15 meters ASL.
|
||||
local Wdir, Wspeed = cv:GetWind( alt or 15 )
|
||||
|
||||
-- Include magnetic declination.
|
||||
if magnetic then
|
||||
@@ -11649,15 +11785,17 @@ function AIRBOSS:_LSOgrade( playerData )
|
||||
local G = GXX .. " " .. GIM .. " " .. " " .. GIC .. " " .. GAR
|
||||
|
||||
-- Count number of minor, normal and major deviations.
|
||||
local N = nXX + nIM + nIC + nAR
|
||||
local nL = count( G, '_' ) / 2
|
||||
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 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 N=nXX+nIM+nIC+nAR
|
||||
local Nv=nXX+nIM
|
||||
local nL=count(G, '_')/2
|
||||
local nS=count(G, '%(')
|
||||
local nN=N-nS-nL
|
||||
local nNv=Nv-nS-nL
|
||||
|
||||
-- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn.
|
||||
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 points
|
||||
@@ -11668,29 +11806,33 @@ function AIRBOSS:_LSOgrade( playerData )
|
||||
G = "Unicorn"
|
||||
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)
|
||||
-- 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
|
||||
-- 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.
|
||||
if nL > 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
|
||||
-- Larger deviations ==> "No grade" 2.0 points.
|
||||
grade = "--"
|
||||
points = 2.0
|
||||
elseif nN > 2 and playerData.actype == AIRBOSS.AircraftCarrier.AV8B then
|
||||
grade="--"
|
||||
points=2.0
|
||||
elseif nNv >= 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
|
||||
-- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections.
|
||||
grade = "(OK)"
|
||||
points = 3.0
|
||||
elseif nL > 0 then
|
||||
grade="(OK)"
|
||||
points=3.0
|
||||
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.
|
||||
grade = "--"
|
||||
points = 2.0
|
||||
elseif nN > 0 then
|
||||
grade="--"
|
||||
points=2.0
|
||||
elseif nN> 0 then
|
||||
-- No larger but average deviations ==> "Fair Pass" Pass with average deviations and corrections.
|
||||
grade = "(OK)"
|
||||
points = 3.0
|
||||
else
|
||||
grade="(OK)"
|
||||
points=3.0
|
||||
else
|
||||
-- Only minor corrections
|
||||
grade = "OK"
|
||||
points = 4.0
|
||||
end
|
||||
grade="OK"
|
||||
points=4.0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -11994,7 +12136,7 @@ function AIRBOSS:_GS( step, n )
|
||||
if n == -1 then
|
||||
gp = AIRBOSS.GroovePos.IC
|
||||
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.INVINCIBLE or 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
|
||||
else
|
||||
gp = AIRBOSS.GroovePos.IW
|
||||
@@ -13873,7 +14015,7 @@ function AIRBOSS:_IsCarrierAircraft( unit )
|
||||
|
||||
-- Special case for Harrier which can only land on Tarawa, LHA and LHD.
|
||||
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.INVINCIBLE or 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
|
||||
else
|
||||
return false
|
||||
@@ -13881,7 +14023,7 @@ function AIRBOSS:_IsCarrierAircraft( unit )
|
||||
end
|
||||
|
||||
-- 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.INVINCIBLE 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
|
||||
return false
|
||||
end
|
||||
@@ -17238,7 +17380,7 @@ function AIRBOSS:_MarkCaseZones( _unitName, flare )
|
||||
end
|
||||
|
||||
-- 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.INVINCIBLE or 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"
|
||||
-- Abeam landing spot zone.
|
||||
local ALSPT = self:_GetZoneAbeamLandingSpot()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -45,14 +45,13 @@
|
||||
-- @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 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
|
||||
|
||||
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # The MSRS Concept
|
||||
--
|
||||
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
|
||||
@@ -95,6 +94,14 @@
|
||||
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
|
||||
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
|
||||
--
|
||||
-- **NOTE on using GOOGLE TTS with SRS:** You need to have the C# library installed in your SRS folder for Google to work.
|
||||
-- You can obtain it e.g. here: [NuGet](https://www.nuget.org/packages/Grpc.Core)
|
||||
--
|
||||
-- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing.
|
||||
-- and also the Google Console error, in case you have missed a step in setting up your Google TTS.
|
||||
-- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"`
|
||||
-- Plays a message on 255AM for the blue coalition in-game.
|
||||
--
|
||||
-- ## Set Voice
|
||||
--
|
||||
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||
@@ -106,6 +113,14 @@
|
||||
--
|
||||
-- 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
|
||||
MSRS = {
|
||||
ClassName = "MSRS",
|
||||
@@ -121,11 +136,12 @@ MSRS = {
|
||||
volume = 1,
|
||||
speed = 1,
|
||||
coordinate = nil,
|
||||
Label = "ROBOT",
|
||||
}
|
||||
|
||||
--- MSRS class version.
|
||||
-- @field #string version
|
||||
MSRS.version="0.0.3"
|
||||
MSRS.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -144,8 +160,9 @@ MSRS.version="0.0.3"
|
||||
-- @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 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
|
||||
function MSRS:New(PathToSRS, Frequency, Modulation)
|
||||
function MSRS:New(PathToSRS, Frequency, Modulation, Volume)
|
||||
|
||||
-- Defaults.
|
||||
Frequency =Frequency or 143
|
||||
@@ -160,6 +177,13 @@ function MSRS:New(PathToSRS, Frequency, Modulation)
|
||||
self:SetModulations(Modulation)
|
||||
self:SetGender()
|
||||
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
|
||||
end
|
||||
@@ -202,12 +226,47 @@ function MSRS:GetPath()
|
||||
return self.path
|
||||
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.
|
||||
-- @param #MSRS self
|
||||
-- @param #number Port Port. Default 5002.
|
||||
-- @return #MSRS self
|
||||
function MSRS:SetPort(Port)
|
||||
self.port=Port or 5002
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get port.
|
||||
@@ -223,6 +282,7 @@ end
|
||||
-- @return #MSRS self
|
||||
function MSRS:SetCoalition(Coalition)
|
||||
self.coalition=Coalition or 0
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get coalition.
|
||||
@@ -391,21 +451,10 @@ function MSRS:PlaySoundFile(Soundfile, Delay)
|
||||
local command=self:_GetCommand()
|
||||
|
||||
-- Append file.
|
||||
command=command.." --file="..tostring(soundfile)
|
||||
command=command..' --file="'..tostring(soundfile)..'"'
|
||||
|
||||
-- Execute command.
|
||||
self:_ExecCommand(command)
|
||||
|
||||
--[[
|
||||
|
||||
command=command.." > bla.txt"
|
||||
|
||||
-- Debug output.
|
||||
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
||||
|
||||
-- Execute SRS command.
|
||||
local x=os.execute(command)
|
||||
|
||||
]]
|
||||
|
||||
end
|
||||
|
||||
@@ -431,16 +480,6 @@ function MSRS:PlaySoundText(SoundText, Delay)
|
||||
|
||||
-- Execute command.
|
||||
self:_ExecCommand(command)
|
||||
|
||||
--[[
|
||||
command=command.." > bla.txt"
|
||||
|
||||
-- Debug putput.
|
||||
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
||||
|
||||
-- Execute SRS command.
|
||||
local x=os.execute(command)
|
||||
]]
|
||||
|
||||
end
|
||||
|
||||
@@ -467,37 +506,48 @@ function MSRS:PlayText(Text, Delay)
|
||||
-- Execute command.
|
||||
self:_ExecCommand(command)
|
||||
|
||||
--[[
|
||||
|
||||
-- Check that length of command is max 255 chars or os.execute() will not work!
|
||||
if string.len(command)>255 then
|
||||
|
||||
-- Create a tmp file.
|
||||
local filename = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".bat"
|
||||
|
||||
local script = io.open(filename, "w+")
|
||||
script:write(command.." && exit")
|
||||
script:close()
|
||||
|
||||
-- Play command.
|
||||
command=string.format("\"%s\"", filename)
|
||||
|
||||
-- Play file in 0.05 seconds
|
||||
timer.scheduleFunction(os.execute, command, timer.getTime()+0.05)
|
||||
|
||||
-- Remove file in 1 second.
|
||||
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||
else
|
||||
|
||||
-- Debug output.
|
||||
self:I(string.format("MSRS Text command=%s", command))
|
||||
end
|
||||
|
||||
-- Execute SRS command.
|
||||
local x=os.execute(command)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Play text message via STTS with explicitly specified options.
|
||||
-- @param #MSRS self
|
||||
-- @param #string Text Text message.
|
||||
-- @param #number Delay Delay in seconds, before the message is played.
|
||||
-- @param #table Frequencies Radio frequencies.
|
||||
-- @param #table Modulations Radio modulations.
|
||||
-- @param #string Gender Gender.
|
||||
-- @param #string Culture Culture.
|
||||
-- @param #string Voice Voice.
|
||||
-- @param #number Volume Volume.
|
||||
-- @param #string Label Label.
|
||||
-- @return #MSRS self
|
||||
function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
|
||||
else
|
||||
|
||||
-- Ensure table.
|
||||
if Frequencies and type(Frequencies)~="table" then
|
||||
Frequencies={Frequencies}
|
||||
end
|
||||
|
||||
-- Ensure table.
|
||||
if Modulations and type(Modulations)~="table" then
|
||||
Modulations={Modulations}
|
||||
end
|
||||
|
||||
-- Get command line.
|
||||
local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label)
|
||||
|
||||
-- Append text.
|
||||
command=command..string.format(" --text=\"%s\"", tostring(Text))
|
||||
|
||||
end
|
||||
-- Execute command.
|
||||
self:_ExecCommand(command)
|
||||
|
||||
]]
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -634,8 +684,9 @@ end
|
||||
-- @param #number volume Volume.
|
||||
-- @param #number speed Speed.
|
||||
-- @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.
|
||||
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 exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
|
||||
@@ -648,6 +699,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
volume=volume or self.volume
|
||||
speed=speed or self.speed
|
||||
port=port or self.port
|
||||
label=label or self.Label
|
||||
|
||||
-- Replace modulation
|
||||
modus=modus:gsub("0", "AM")
|
||||
@@ -657,12 +709,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)
|
||||
|
||||
-- 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")
|
||||
|
||||
-- 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" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
|
||||
|
||||
-- Set voice or gender/culture.
|
||||
if voice then
|
||||
@@ -671,7 +723,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
else
|
||||
-- Add gender.
|
||||
if gender and gender~="female" then
|
||||
command=command..string.format(" --gender=%s", tostring(gender))
|
||||
command=command..string.format(" -g %s", tostring(gender))
|
||||
end
|
||||
-- Add culture.
|
||||
if culture and culture~="en-GB" then
|
||||
@@ -696,6 +748,353 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
return command
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Manages radio transmissions.
|
||||
--
|
||||
-- The purpose of the MSRSQUEUE class is to manage SRS text-to-speech (TTS) messages using the MSRS class.
|
||||
-- This can be used to submit multiple TTS messages and the class takes care that they are transmitted one after the other (and not overlapping).
|
||||
--
|
||||
-- @type MSRSQUEUE
|
||||
-- @field #string ClassName Name of the class "MSRSQUEUE".
|
||||
-- @field #string lid ID for dcs.log.
|
||||
-- @field #table queue The queue of transmissions.
|
||||
-- @field #string alias Name of the radio queue.
|
||||
-- @field #number dt Time interval in seconds for checking the radio queue.
|
||||
-- @field #number Tlast Time (abs) when the last transmission finished.
|
||||
-- @field #boolean checking If `true`, the queue update function is scheduled to be called again.
|
||||
-- @extends Core.Base#BASE
|
||||
MSRSQUEUE = {
|
||||
ClassName = "MSRSQUEUE",
|
||||
Debugmode = nil,
|
||||
lid = nil,
|
||||
queue = {},
|
||||
alias = nil,
|
||||
dt = nil,
|
||||
Tlast = nil,
|
||||
checking = nil,
|
||||
}
|
||||
|
||||
--- Radio queue transmission data.
|
||||
-- @type MSRSQUEUE.Transmission
|
||||
-- @field #string text Text to be transmitted.
|
||||
-- @field Sound.SRS#MSRS msrs MOOSE SRS object.
|
||||
-- @field #number duration Duration in seconds.
|
||||
-- @field #table subgroups Groups to send subtitle to.
|
||||
-- @field #string subtitle Subtitle of the transmission.
|
||||
-- @field #number subduration Duration of the subtitle being displayed.
|
||||
-- @field #number frequency Frequency.
|
||||
-- @field #number modulation Modulation.
|
||||
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
|
||||
-- @field #boolean isplaying If true, transmission is currently playing.
|
||||
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||
-- @field #number interval Interval in seconds before next transmission.
|
||||
|
||||
--- Create a new MSRSQUEUE object for a given radio frequency/modulation.
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @param #string alias (Optional) Name of the radio queue.
|
||||
-- @return #MSRSQUEUE self The MSRSQUEUE object.
|
||||
function MSRSQUEUE:New(alias)
|
||||
|
||||
-- Inherit base
|
||||
local self=BASE:Inherit(self, BASE:New()) --#MSRSQUEUE
|
||||
|
||||
self.alias=alias or "My Radio"
|
||||
|
||||
self.dt=1.0
|
||||
|
||||
self.lid=string.format("MSRSQUEUE %s | ", self.alias)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Clear the radio queue.
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @return #MSRSQUEUE self The MSRSQUEUE object.
|
||||
function MSRSQUEUE:Clear()
|
||||
self:I(self.lid.."Clearning MSRSQUEUE")
|
||||
self.queue={}
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Add a transmission to the radio queue.
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @param #MSRSQUEUE.Transmission transmission The transmission data table.
|
||||
-- @return #MSRSQUEUE self
|
||||
function MSRSQUEUE:AddTransmission(transmission)
|
||||
|
||||
-- Init.
|
||||
transmission.isplaying=false
|
||||
transmission.Tstarted=nil
|
||||
|
||||
-- Add to queue.
|
||||
table.insert(self.queue, transmission)
|
||||
|
||||
-- Start checking.
|
||||
if not self.checking then
|
||||
self:_CheckRadioQueue()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new transmission and add it to the radio queue.
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @param #string text Text to play.
|
||||
-- @param #number duration Duration in seconds the file lasts. Default is determined by number of characters of the text message.
|
||||
-- @param Sound.SRS#MSRS msrs MOOSE SRS object.
|
||||
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||
-- @param #table subgroups Groups that should receive the subtiltle.
|
||||
-- @param #string subtitle Subtitle displayed when the message is played.
|
||||
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
||||
-- @param #number frequency Radio frequency if other than MSRS default.
|
||||
-- @param #number modulation Radio modulation if other then MSRS default.
|
||||
-- @return #MSRSQUEUE.Transmission Radio transmission table.
|
||||
function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation)
|
||||
|
||||
-- Sanity checks.
|
||||
if not text then
|
||||
self:E(self.lid.."ERROR: No text specified.")
|
||||
return nil
|
||||
end
|
||||
if type(text)~="string" then
|
||||
self:E(self.lid.."ERROR: Text specified is NOT a string.")
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- Create a new transmission object.
|
||||
local transmission={} --#MSRSQUEUE.Transmission
|
||||
transmission.text=text
|
||||
transmission.duration=duration or STTS.getSpeechTime(text)
|
||||
transmission.msrs=msrs
|
||||
transmission.Tplay=tstart or timer.getAbsTime()
|
||||
transmission.subtitle=subtitle
|
||||
transmission.interval=interval or 0
|
||||
transmission.frequency=frequency
|
||||
transmission.modulation=modulation
|
||||
transmission.subgroups=subgroups
|
||||
if transmission.subtitle then
|
||||
transmission.subduration=subduration or transmission.duration
|
||||
else
|
||||
transmission.subduration=0 --nil
|
||||
end
|
||||
|
||||
-- Add transmission to queue.
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
return transmission
|
||||
end
|
||||
|
||||
--- Broadcast radio message.
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @param #MSRSQUEUE.Transmission transmission The transmission.
|
||||
function MSRSQUEUE:Broadcast(transmission)
|
||||
|
||||
if transmission.frequency then
|
||||
transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, Gender, Culture, Voice, Volume, Label)
|
||||
else
|
||||
transmission.msrs:PlayText(transmission.text)
|
||||
end
|
||||
|
||||
local function texttogroup(gid)
|
||||
-- Text to group.
|
||||
trigger.action.outTextForGroup(gid, transmission.subtitle, transmission.subduration, true)
|
||||
end
|
||||
|
||||
if transmission.subgroups and #transmission.subgroups>0 then
|
||||
|
||||
for _,_group in pairs(transmission.subgroups) do
|
||||
local group=_group --Wrapper.Group#GROUP
|
||||
|
||||
if group and group:IsAlive() then
|
||||
local gid=group:GetID()
|
||||
|
||||
self:ScheduleOnce(4, texttogroup, gid)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Calculate total transmission duration of all transmission in the queue.
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @return #number Total transmission duration.
|
||||
function MSRSQUEUE:CalcTransmisstionDuration()
|
||||
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
local T=0
|
||||
for _,_transmission in pairs(self.queue) do
|
||||
local transmission=_transmission --#MSRSQUEUE.Transmission
|
||||
|
||||
if transmission.isplaying then
|
||||
|
||||
-- Playing for dt seconds.
|
||||
local dt=Tnow-transmission.Tstarted
|
||||
|
||||
T=T+transmission.duration-dt
|
||||
|
||||
else
|
||||
T=T+transmission.duration
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return T
|
||||
end
|
||||
|
||||
--- Check radio queue for transmissions to be broadcasted.
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @param #number delay Delay in seconds before checking.
|
||||
function MSRSQUEUE:_CheckRadioQueue(delay)
|
||||
|
||||
-- Transmissions in queue.
|
||||
local N=#self.queue
|
||||
|
||||
-- Debug info.
|
||||
self:T2(self.lid..string.format("Check radio queue %s: delay=%.3f sec, N=%d, checking=%s", self.alias, delay or 0, N, tostring(self.checking)))
|
||||
|
||||
if delay and delay>0 then
|
||||
|
||||
-- Delayed call.
|
||||
self:ScheduleOnce(delay, MSRSQUEUE._CheckRadioQueue, self)
|
||||
|
||||
-- Checking on.
|
||||
self.checking=true
|
||||
|
||||
else
|
||||
|
||||
-- Check if queue is empty.
|
||||
if N==0 then
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
|
||||
|
||||
-- Queue is now empty. Nothing to else to do. We start checking again, if a transmission is added.
|
||||
self.checking=false
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Get current abs time.
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
-- Checking on.
|
||||
self.checking=true
|
||||
|
||||
-- Set dt.
|
||||
local dt=self.dt
|
||||
|
||||
|
||||
local playing=false
|
||||
local next=nil --#MSRSQUEUE.Transmission
|
||||
local remove=nil
|
||||
for i,_transmission in ipairs(self.queue) do
|
||||
local transmission=_transmission --#MSRSQUEUE.Transmission
|
||||
|
||||
-- Check if transmission time has passed.
|
||||
if time>=transmission.Tplay then
|
||||
|
||||
-- Check if transmission is currently playing.
|
||||
if transmission.isplaying then
|
||||
|
||||
-- Check if transmission is finished.
|
||||
if time>=transmission.Tstarted+transmission.duration then
|
||||
|
||||
-- Transmission over.
|
||||
transmission.isplaying=false
|
||||
|
||||
-- Remove ith element in queue.
|
||||
remove=i
|
||||
|
||||
-- Store time last transmission finished.
|
||||
self.Tlast=time
|
||||
|
||||
else -- still playing
|
||||
|
||||
-- Transmission is still playing.
|
||||
playing=true
|
||||
|
||||
dt=transmission.duration-(time-transmission.Tstarted)
|
||||
|
||||
end
|
||||
|
||||
else -- not playing yet
|
||||
|
||||
local Tlast=self.Tlast
|
||||
|
||||
if transmission.interval==nil then
|
||||
|
||||
-- Not playing ==> this will be next.
|
||||
if next==nil then
|
||||
next=transmission
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if Tlast==nil or time-Tlast>=transmission.interval then
|
||||
next=transmission
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- We got a transmission or one with an interval that is not due yet. No need for anything else.
|
||||
if next or Tlast then
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Transmission not due yet.
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Found a new transmission.
|
||||
if next~=nil and not playing then
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f", next.text, time))
|
||||
|
||||
-- Call SRS.
|
||||
self:Broadcast(next)
|
||||
|
||||
next.isplaying=true
|
||||
next.Tstarted=time
|
||||
dt=next.duration
|
||||
end
|
||||
|
||||
-- Remove completed call from queue.
|
||||
if remove then
|
||||
-- Remove from queue.
|
||||
table.remove(self.queue, remove)
|
||||
N=N-1
|
||||
|
||||
-- Check if queue is empty.
|
||||
if #self.queue==0 then
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
|
||||
|
||||
self.checking=false
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Check queue.
|
||||
self:_CheckRadioQueue(dt)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -183,13 +183,17 @@ do -- Sound File
|
||||
|
||||
--- Set path, where the sound file is located.
|
||||
-- @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
|
||||
function SOUNDFILE:SetPath(Path)
|
||||
|
||||
-- Init path.
|
||||
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.
|
||||
local nmax=1000 ; local n=1
|
||||
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
|
||||
|
||||
@@ -40,7 +40,7 @@ do -- UserSound
|
||||
-- @param #USERSOUND self
|
||||
-- @param #string UserSoundFileName The filename of the usersound.
|
||||
-- @return #USERSOUND
|
||||
function USERSOUND:New( UserSoundFileName ) --R2.3
|
||||
function USERSOUND:New( UserSoundFileName )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
|
||||
|
||||
@@ -58,7 +58,7 @@ do -- UserSound
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- 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
|
||||
|
||||
@@ -75,7 +75,7 @@ do -- UserSound
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:ToAll() -- Play the sound that Blue has won.
|
||||
--
|
||||
function USERSOUND:ToAll() --R2.3
|
||||
function USERSOUND:ToAll()
|
||||
|
||||
trigger.action.outSound( self.UserSoundFileName )
|
||||
|
||||
@@ -91,7 +91,7 @@ do -- UserSound
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- 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 )
|
||||
|
||||
@@ -107,7 +107,7 @@ do -- UserSound
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- 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 )
|
||||
|
||||
@@ -123,9 +123,9 @@ do -- UserSound
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- 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
|
||||
if Delay>0 then
|
||||
@@ -136,5 +136,27 @@ do -- UserSound
|
||||
|
||||
return self
|
||||
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
|
||||
@@ -1,18 +1,18 @@
|
||||
--- **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.
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
-- 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
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
||||
--
|
||||
--
|
||||
-- @module ENUMS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
-- @type ENUMS
|
||||
|
||||
--- Because ENUMS are just better practice.
|
||||
--
|
||||
--
|
||||
-- The ENUMS class adds some handy variables, which help you to make your code better and more general.
|
||||
--
|
||||
-- @field #ENUMS
|
||||
@@ -30,16 +30,16 @@ ENUMS = {}
|
||||
-- @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 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 WeaponHold AI will hold fire under all circumstances.
|
||||
ENUMS.ROE = {
|
||||
WeaponFree = 0,
|
||||
OpenFireWeaponFree = 1,
|
||||
OpenFire = 2,
|
||||
ReturnFire = 3,
|
||||
WeaponHold = 4,
|
||||
}
|
||||
WeaponFree=0,
|
||||
OpenFireWeaponFree=1,
|
||||
OpenFire=2,
|
||||
ReturnFire=3,
|
||||
WeaponHold=4,
|
||||
}
|
||||
|
||||
--- Reaction On Threat.
|
||||
-- @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 AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
|
||||
ENUMS.ROT = {
|
||||
NoReaction = 0,
|
||||
PassiveDefense = 1,
|
||||
EvadeFire = 2,
|
||||
BypassAndEscape = 3,
|
||||
AllowAbortMission = 4,
|
||||
NoReaction=0,
|
||||
PassiveDefense=1,
|
||||
EvadeFire=2,
|
||||
BypassAndEscape=3,
|
||||
AllowAbortMission=4,
|
||||
}
|
||||
|
||||
--- Alarm state.
|
||||
@@ -62,12 +62,12 @@ ENUMS.ROT = {
|
||||
-- @field #number Green Group is not combat ready. Sensors are stowed if possible.
|
||||
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
|
||||
ENUMS.AlarmState = {
|
||||
Auto = 0,
|
||||
Green = 1,
|
||||
Red = 2,
|
||||
Auto=0,
|
||||
Green=1,
|
||||
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
|
||||
ENUMS.WeaponFlag={
|
||||
-- Bombs
|
||||
@@ -111,7 +111,7 @@ ENUMS.WeaponFlag={
|
||||
--
|
||||
-- Bombs
|
||||
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)
|
||||
--- Rockets
|
||||
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
|
||||
@@ -123,9 +123,11 @@ ENUMS.WeaponFlag={
|
||||
--- Air-To-Air Missiles
|
||||
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
|
||||
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
|
||||
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||
--- Guns
|
||||
Cannons = 805306368, -- GUN_POD + BuiltInCannon
|
||||
--- Torpedo
|
||||
Torpedo = 4294967296,
|
||||
---
|
||||
-- Even More Genral
|
||||
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
|
||||
@@ -133,9 +135,96 @@ ENUMS.WeaponFlag={
|
||||
AnyAG = 2956984318, -- Any Air-To-Ground Weapon
|
||||
AnyAA = 264241152, -- Any Air-To-Air 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.
|
||||
-- @type ENUMS.MissionTask
|
||||
-- @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",
|
||||
}
|
||||
|
||||
--- 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
|
||||
ENUMS.Formation={}
|
||||
ENUMS.Formation.FixedWing={}
|
||||
@@ -216,23 +305,23 @@ ENUMS.Formation.FixedWing.FighterVic.Close = 917505
|
||||
ENUMS.Formation.FixedWing.FighterVic.Open = 917506
|
||||
ENUMS.Formation.RotaryWing={}
|
||||
ENUMS.Formation.RotaryWing.Column={}
|
||||
ENUMS.Formation.RotaryWing.Column.D70 = 720896
|
||||
ENUMS.Formation.RotaryWing.Column.D70=720896
|
||||
ENUMS.Formation.RotaryWing.Wedge={}
|
||||
ENUMS.Formation.RotaryWing.Wedge.D70 = 8
|
||||
ENUMS.Formation.RotaryWing.Wedge.D70=8
|
||||
ENUMS.Formation.RotaryWing.FrontRight={}
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D300 = 655361
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D600 = 655362
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D300=655361
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D600=655362
|
||||
ENUMS.Formation.RotaryWing.FrontLeft={}
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D300 = 655617
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D600 = 655618
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
|
||||
ENUMS.Formation.RotaryWing.EchelonRight={}
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D70 = 589825
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D300 = 589826
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D600 = 589827
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft={}
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 = 590081
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300 = 590082
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600 = 590083
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
|
||||
ENUMS.Formation.Vehicle={}
|
||||
ENUMS.Formation.Vehicle.Vee="Vee"
|
||||
ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
|
||||
@@ -244,34 +333,34 @@ ENUMS.Formation.Vehicle.Cone="Cone"
|
||||
ENUMS.Formation.Vehicle.Diamond="Diamond"
|
||||
|
||||
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
|
||||
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki.
|
||||
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
||||
-- @type ENUMS.FormationOld
|
||||
ENUMS.FormationOld={}
|
||||
ENUMS.FormationOld.FixedWing={}
|
||||
ENUMS.FormationOld.FixedWing.LineAbreast = 1
|
||||
ENUMS.FormationOld.FixedWing.Trail = 2
|
||||
ENUMS.FormationOld.FixedWing.Wedge = 3
|
||||
ENUMS.FormationOld.FixedWing.EchelonRight = 4
|
||||
ENUMS.FormationOld.FixedWing.EchelonLeft = 5
|
||||
ENUMS.FormationOld.FixedWing.FingerFour = 6
|
||||
ENUMS.FormationOld.FixedWing.SpreadFour = 7
|
||||
ENUMS.FormationOld.FixedWing.BomberElement = 12
|
||||
ENUMS.FormationOld.FixedWing.BomberElementHeight = 13
|
||||
ENUMS.FormationOld.FixedWing.FighterVic = 14
|
||||
ENUMS.FormationOld.FixedWing.LineAbreast=1
|
||||
ENUMS.FormationOld.FixedWing.Trail=2
|
||||
ENUMS.FormationOld.FixedWing.Wedge=3
|
||||
ENUMS.FormationOld.FixedWing.EchelonRight=4
|
||||
ENUMS.FormationOld.FixedWing.EchelonLeft=5
|
||||
ENUMS.FormationOld.FixedWing.FingerFour=6
|
||||
ENUMS.FormationOld.FixedWing.SpreadFour=7
|
||||
ENUMS.FormationOld.FixedWing.BomberElement=12
|
||||
ENUMS.FormationOld.FixedWing.BomberElementHeight=13
|
||||
ENUMS.FormationOld.FixedWing.FighterVic=14
|
||||
ENUMS.FormationOld.RotaryWing={}
|
||||
ENUMS.FormationOld.RotaryWing.Wedge = 8
|
||||
ENUMS.FormationOld.RotaryWing.Echelon = 9
|
||||
ENUMS.FormationOld.RotaryWing.Front = 10
|
||||
ENUMS.FormationOld.RotaryWing.Column = 11
|
||||
ENUMS.FormationOld.RotaryWing.Wedge=8
|
||||
ENUMS.FormationOld.RotaryWing.Echelon=9
|
||||
ENUMS.FormationOld.RotaryWing.Front=10
|
||||
ENUMS.FormationOld.RotaryWing.Column=11
|
||||
|
||||
|
||||
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
|
||||
--
|
||||
--
|
||||
-- * Short pulse "*"
|
||||
-- * Long pulse "-"
|
||||
--
|
||||
--
|
||||
-- Pulses are separated by a blank character " ".
|
||||
--
|
||||
--
|
||||
-- @type ENUMS.Morse
|
||||
ENUMS.Morse={}
|
||||
ENUMS.Morse.A="* -"
|
||||
@@ -313,9 +402,9 @@ ENUMS.Morse.N0="- - - - -"
|
||||
ENUMS.Morse[" "]=" "
|
||||
|
||||
--- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
|
||||
--
|
||||
--
|
||||
-- @type ENUMS.ISOLang
|
||||
ENUMS.ISOLang =
|
||||
ENUMS.ISOLang =
|
||||
{
|
||||
Arabic = 'AR',
|
||||
Chinese = 'ZH',
|
||||
@@ -329,7 +418,7 @@ ENUMS.ISOLang =
|
||||
}
|
||||
|
||||
--- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet).
|
||||
--
|
||||
--
|
||||
-- @type ENUMS.Phonetic
|
||||
ENUMS.Phonetic =
|
||||
{
|
||||
@@ -359,4 +448,114 @@ ENUMS.Phonetic =
|
||||
X = 'Xray',
|
||||
Y = 'Yankee',
|
||||
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",
|
||||
}
|
||||
}
|
||||
|
||||
773
Moose Development/Moose/Utilities/FiFo.lua
Normal file
773
Moose Development/Moose/Utilities/FiFo.lua
Normal 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
@@ -697,6 +697,34 @@ function CONTROLLABLE:CommandActivateICLS( Channel, UnitID, Callsign, Delay )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Activate LINK4 system of the CONTROLLABLE. The controllable should be an aircraft carrier!
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Frequency Link4 Frequency in MHz, e.g. 336
|
||||
-- @param #number UnitID The DCS UNIT ID of the unit the LINK4 system is attached to. Useful if more units are in one group.
|
||||
-- @param #string Callsign Morse code identification callsign.
|
||||
-- @param #number Delay (Optional) Delay in seconds before the LINK4 is deactivated.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay)
|
||||
|
||||
-- Command to activate Link4 system.
|
||||
local CommandActivateLink4= {
|
||||
id = "ActivateLink4",
|
||||
params= {
|
||||
["frequency "] = Frequency*1000,
|
||||
["unitId"] = UnitID,
|
||||
["name"] = Callsign,
|
||||
}
|
||||
}
|
||||
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.CommandActivateLink4, {self}, Delay)
|
||||
else
|
||||
self:SetCommand(CommandActivateLink4)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Deactivate the active beacon of the CONTROLLABLE.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Delay (Optional) Delay in seconds before the beacon is deactivated.
|
||||
@@ -707,7 +735,7 @@ function CONTROLLABLE:CommandDeactivateBeacon( Delay )
|
||||
local CommandDeactivateBeacon = { id = 'DeactivateBeacon', params = {} }
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.CommandActivateBeacon, { self }, Delay )
|
||||
SCHEDULER:New( nil, self.CommandDeactivateBeacon, { self }, Delay )
|
||||
else
|
||||
self:SetCommand( CommandDeactivateBeacon )
|
||||
end
|
||||
@@ -733,6 +761,24 @@ function CONTROLLABLE:CommandDeactivateICLS( Delay )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Deactivate the active Link4 of the CONTROLLABLE.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Delay (Optional) Delay in seconds before the Link4 is deactivated.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:CommandDeactivateLink4(Delay)
|
||||
|
||||
-- Command to deactivate
|
||||
local CommandDeactivateLink4={id='DeactivateLink4', params={}}
|
||||
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.CommandDeactivateLink4, {self}, Delay)
|
||||
else
|
||||
self:SetCommand(CommandDeactivateLink4)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set callsign of the CONTROLLABLE. See [DCS command setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign)
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param DCS#CALLSIGN CallName Number corresponding the the callsign identifier you wish this group to be called.
|
||||
@@ -3770,3 +3816,44 @@ function POSITIONABLE:IsSubmarine()
|
||||
|
||||
return nil
|
||||
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
|
||||
@@ -384,7 +384,7 @@ end
|
||||
-- So all event listeners will catch the destroy event of this group for each unit in the group.
|
||||
-- To raise these events, provide the `GenerateEvent` parameter.
|
||||
-- @param #GROUP self
|
||||
-- @param #boolean GenerateEvent 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.
|
||||
-- @usage
|
||||
-- -- 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()
|
||||
|
||||
if DCSGroup then
|
||||
local DCSUnits = DCSGroup:getUnits()
|
||||
local DCSUnits = DCSGroup:getUnits() or {}
|
||||
local Units = {}
|
||||
for Index, UnitData in pairs( DCSUnits ) do
|
||||
Units[#Units+1] = UNIT:Find( UnitData )
|
||||
@@ -680,6 +680,30 @@ function GROUP:GetUnit( UnitNumber )
|
||||
return nil
|
||||
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.
|
||||
-- If the underlying DCS Unit does not exist, the method will return nil. .
|
||||
-- @param #GROUP self
|
||||
@@ -687,11 +711,24 @@ end
|
||||
-- @return DCS#Unit The DCS Unit.
|
||||
function GROUP:GetDCSUnit( UnitNumber )
|
||||
|
||||
local DCSGroup=self:GetDCSObject()
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
|
||||
return DCSUnitFound
|
||||
|
||||
if DCSGroup.getUnit and DCSGroup:getUnit( UnitNumber ) then
|
||||
return DCSGroup:getUnit( UnitNumber )
|
||||
else
|
||||
|
||||
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
|
||||
if _unit and _unit:isExist() then
|
||||
return _unit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -765,8 +802,7 @@ end
|
||||
|
||||
--- Returns the average velocity Vec3 vector.
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @return DCS#Vec3 The velocity Vec3 vector
|
||||
-- @return #nil The GROUP is not existing or alive.
|
||||
-- @return DCS#Vec3 The velocity Vec3 vector or `#nil` if the GROUP is not existing or alive.
|
||||
function GROUP:GetVelocityVec3()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@@ -797,11 +833,19 @@ function GROUP:GetVelocityVec3()
|
||||
return nil
|
||||
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.
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level.
|
||||
-- @return DCS#Vec3 The height of the group or nil if is not existing or alive.
|
||||
-- @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 height of the group or nil if is not existing or alive.
|
||||
function GROUP:GetHeight( FromGround )
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@@ -901,6 +945,24 @@ function GROUP:GetTypeName()
|
||||
return nil
|
||||
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.
|
||||
-- @param #GROUP self
|
||||
-- @return #string The player name of the group.
|
||||
@@ -994,24 +1056,32 @@ end
|
||||
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
|
||||
function GROUP:GetCoordinate()
|
||||
|
||||
local FirstUnit = self:GetUnit(1)
|
||||
local Units = self:GetUnits() or {}
|
||||
|
||||
if FirstUnit then
|
||||
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
|
||||
return FirstUnitCoordinate
|
||||
for _,_unit in pairs(Units) do
|
||||
local FirstUnit = _unit -- Wrapper.Unit#UNIT
|
||||
|
||||
if FirstUnit then
|
||||
|
||||
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
|
||||
|
||||
if FirstUnitCoordinate then
|
||||
local Heading = self:GetHeading()
|
||||
FirstUnitCoordinate.Heading = Heading
|
||||
return FirstUnitCoordinate
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
|
||||
|
||||
return nil
|
||||
|
||||
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.
|
||||
-- @param #GROUP self
|
||||
-- @param #number Radius
|
||||
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP.
|
||||
-- @return #nil The GROUP is invalid or empty
|
||||
-- @param #number Radius Radius in meters.
|
||||
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP or #nil The GROUP is invalid or empty.
|
||||
-- @usage
|
||||
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
|
||||
function GROUP:GetRandomVec3(Radius)
|
||||
@@ -1032,24 +1102,25 @@ end
|
||||
|
||||
--- Returns the mean heading of every UNIT in the GROUP in degrees
|
||||
-- @param #GROUP self
|
||||
-- @return #number mean heading of the GROUP
|
||||
-- @return #nil The first UNIT is not existing or alive.
|
||||
-- @return #number Mean heading of the GROUP in degrees or #nil The first UNIT is not existing or alive.
|
||||
function GROUP:GetHeading()
|
||||
self:F2(self.GroupName)
|
||||
|
||||
self:F2(self.GroupName)
|
||||
|
||||
local GroupSize = self:GetSize()
|
||||
local HeadingAccumulator = 0
|
||||
|
||||
local n=0
|
||||
local Units = self:GetUnits()
|
||||
|
||||
if GroupSize then
|
||||
for i = 1, GroupSize do
|
||||
local unit=self:GetUnit(i)
|
||||
for _,unit in pairs(Units) do
|
||||
if unit and unit:IsAlive() then
|
||||
HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
|
||||
n=n+1
|
||||
end
|
||||
end
|
||||
return math.floor(HeadingAccumulator / n)
|
||||
return math.floor(HeadingAccumulator / n)
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } )
|
||||
@@ -1061,8 +1132,8 @@ end
|
||||
--- Return the fuel state and unit reference for the unit with the least
|
||||
-- amount of fuel in the group.
|
||||
-- @param #GROUP self
|
||||
-- @return #number The fuel state of the unit with the least amount of fuel
|
||||
-- @return #Unit reference to #Unit object for further processing
|
||||
-- @return #number The fuel state of the unit with the least amount of fuel.
|
||||
-- @return Wrapper.Unit#UNIT reference to #Unit object for further processing.
|
||||
function GROUP:GetFuelMin()
|
||||
self:F3(self.ControllableName)
|
||||
|
||||
@@ -1104,7 +1175,7 @@ function GROUP:GetFuelAvg()
|
||||
local TotalFuel = 0
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
local UnitFuel = Unit:GetFuel()
|
||||
local UnitFuel = Unit:GetFuel() or 0
|
||||
self:F( { Fuel = UnitFuel } )
|
||||
TotalFuel = TotalFuel + UnitFuel
|
||||
end
|
||||
@@ -1181,13 +1252,14 @@ function GROUP:IsInZone( Zone )
|
||||
for UnitID, UnitData in pairs(self:GetUnits()) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
|
||||
-- Get 2D vector. That's all we need for the zone check.
|
||||
local vec2=Unit:GetVec2()
|
||||
local vec2 = nil
|
||||
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.
|
||||
else
|
||||
-- This one is not but another could be.
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2609,6 +2681,41 @@ function GROUP:GetSkill()
|
||||
return skill
|
||||
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
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
|
||||
@@ -382,7 +382,8 @@ function POSITIONABLE:GetCoordinate()
|
||||
local PositionableVec3 = self:GetVec3()
|
||||
|
||||
local coord = COORDINATE:NewFromVec3( PositionableVec3 )
|
||||
|
||||
local heading = self:GetHeading()
|
||||
coord.Heading = heading
|
||||
-- Return a new coordinate object.
|
||||
return coord
|
||||
|
||||
@@ -536,7 +537,7 @@ function POSITIONABLE:GetBoundingRadius( MinDist )
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the altitude of the POSITIONABLE.
|
||||
--- Returns the altitude above sea level of the POSITIONABLE.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return DCS#Distance The altitude of the POSITIONABLE.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
@@ -778,7 +779,7 @@ function POSITIONABLE:GetRelativeVelocity( Positionable )
|
||||
return UTILS.VecNorm( vtot )
|
||||
end
|
||||
|
||||
--- Returns the POSITIONABLE height in meters.
|
||||
--- Returns the POSITIONABLE height above sea level in meters.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return DCS#Vec3 The height of the POSITIONABLE in meters.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
@@ -1176,6 +1177,30 @@ function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
|
||||
return nil
|
||||
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}.
|
||||
-- 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
|
||||
@@ -1249,6 +1274,30 @@ function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Nam
|
||||
return nil
|
||||
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}.
|
||||
-- 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
|
||||
@@ -1551,7 +1600,11 @@ do -- Cargo
|
||||
["Ural-4320 APA-5D"] = 10,
|
||||
["Ural-4320T"] = 14,
|
||||
["ZBD04A"] = 7, -- new by kappa
|
||||
["VAB_Mephisto"] = 8 -- new by Apple
|
||||
["VAB_Mephisto"] = 8, -- new by Apple
|
||||
["tt_KORD"] = 6, -- 2.7.1 HL/TT
|
||||
["tt_DSHK"] = 6,
|
||||
["HL_KORD"] = 6,
|
||||
["HL_DSHK"] = 6,
|
||||
}
|
||||
|
||||
local CargoBayWeightLimit = (Weights[Desc.typeName] or 0) * 95
|
||||
|
||||
@@ -1,46 +1,52 @@
|
||||
--- **Wrapper** -- STATIC wraps the DCS StaticObject class.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
--
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Wrapper.Static
|
||||
-- @image Wrapper_Static.JPG
|
||||
|
||||
|
||||
--- @type STATIC
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
--- Wrapper class to handle Static objects.
|
||||
--
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- * Wraps the DCS Static objects.
|
||||
-- * Support all DCS Static APIs.
|
||||
-- * Enhance with Static specific APIs not in the DCS API set.
|
||||
--
|
||||
--
|
||||
-- ## STATIC reference methods
|
||||
--
|
||||
--
|
||||
-- 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).
|
||||
--
|
||||
--
|
||||
-- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
|
||||
-- using the Static Name.
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- * @{#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
|
||||
STATIC = { ClassName = "STATIC" }
|
||||
STATIC = {
|
||||
ClassName = "STATIC",
|
||||
}
|
||||
|
||||
|
||||
--- Register a static object.
|
||||
-- @param #STATIC self
|
||||
@@ -52,6 +58,7 @@ function STATIC:Register( StaticName )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Finds a STATIC from the _DATABASE using a DCSStatic object.
|
||||
-- @param #STATIC self
|
||||
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
|
||||
@@ -76,13 +83,13 @@ function STATIC:FindByName( StaticName, RaiseError )
|
||||
|
||||
-- Set static name.
|
||||
self.StaticName = StaticName
|
||||
|
||||
|
||||
if StaticFound then
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
if RaiseError == nil or RaiseError == true then
|
||||
error( "STATIC not found for: " .. StaticName )
|
||||
|
||||
if RaiseError == nil or RaiseError == true then
|
||||
error( "STATIC not found for: " .. StaticName )
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -90,39 +97,38 @@ end
|
||||
|
||||
--- Destroys the STATIC.
|
||||
-- @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.
|
||||
-- @return #nil The DCS StaticObject is not existing or alive.
|
||||
--
|
||||
-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static.
|
||||
-- @return #nil The DCS StaticObject is not existing or alive.
|
||||
-- @usage
|
||||
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
|
||||
-- Helicopter = STATIC:FindByName( "Helicopter" )
|
||||
-- Helicopter:Destroy( true )
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- Ground static example: destroy the static Tank and generate a S_EVENT_DEAD.
|
||||
-- Tanks = UNIT:FindByName( "Tank" )
|
||||
-- Tanks:Destroy( true )
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- Ship static example: destroy the Ship silently.
|
||||
-- Ship = STATIC:FindByName( "Ship" )
|
||||
-- Ship:Destroy()
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- Destroy without event generation example.
|
||||
-- Ship = STATIC:FindByName( "Boat" )
|
||||
-- Ship:Destroy( false ) -- Don't generate any event upon destruction.
|
||||
--
|
||||
-- Ship:Destroy( false ) -- Don't generate an event upon destruction.
|
||||
--
|
||||
function STATIC:Destroy( GenerateEvent )
|
||||
self:F2( self.ObjectName )
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
|
||||
|
||||
if DCSObject then
|
||||
|
||||
|
||||
local StaticName = DCSObject:getName()
|
||||
self:F( { StaticName = StaticName } )
|
||||
|
||||
|
||||
if GenerateEvent and GenerateEvent == true then
|
||||
if self:IsAir() then
|
||||
self:CreateEventCrash( timer.getTime(), DCSObject )
|
||||
@@ -134,7 +140,7 @@ function STATIC:Destroy( GenerateEvent )
|
||||
else
|
||||
self:CreateEventRemoveUnit( timer.getTime(), DCSObject )
|
||||
end
|
||||
|
||||
|
||||
DCSObject:destroy()
|
||||
return true
|
||||
end
|
||||
@@ -142,16 +148,17 @@ function STATIC:Destroy( GenerateEvent )
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get DCS object of static of static.
|
||||
-- @param #STATIC self
|
||||
-- @return DCS static object
|
||||
function STATIC:GetDCSObject()
|
||||
local DCSStatic = StaticObject.getByName( self.StaticName )
|
||||
|
||||
|
||||
if DCSStatic then
|
||||
return DCSStatic
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -163,7 +170,7 @@ function STATIC:GetUnits()
|
||||
local DCSStatic = self:GetDCSObject()
|
||||
|
||||
local Statics = {}
|
||||
|
||||
|
||||
if DCSStatic then
|
||||
Statics[1] = STATIC:Find( DCSStatic )
|
||||
self:T3( Statics )
|
||||
@@ -173,6 +180,7 @@ function STATIC:GetUnits()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get threat level of static.
|
||||
-- @param #STATIC self
|
||||
-- @return #number Threat level 1.
|
||||
@@ -186,62 +194,65 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
|
||||
-- @param #number Delay Delay in seconds before the static is spawned.
|
||||
function STATIC:SpawnAt( Coordinate, Heading, Delay )
|
||||
function STATIC:SpawnAt(Coordinate, Heading, Delay)
|
||||
|
||||
Heading = Heading or 0
|
||||
Heading=Heading or 0
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.SpawnAt, { self, Coordinate, Heading }, Delay )
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
else
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
|
||||
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
|
||||
|
||||
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
|
||||
-- This is useful to respawn a cargo after it has been destroyed.
|
||||
-- @param #STATIC self
|
||||
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawn( CountryID, Delay )
|
||||
function STATIC:ReSpawn(CountryID, Delay)
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.ReSpawn, { self, CountryID }, Delay )
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
|
||||
else
|
||||
|
||||
CountryID = CountryID or self:GetCountry()
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, CountryID )
|
||||
|
||||
SpawnStatic:Spawn( nil, self.StaticName )
|
||||
CountryID=CountryID or self:GetCountry()
|
||||
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
|
||||
|
||||
SpawnStatic:Spawn(nil, self.StaticName)
|
||||
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
|
||||
-- @param #STATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default is now.
|
||||
function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
|
||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
|
||||
|
||||
-- Heading=Heading or 0
|
||||
--Heading=Heading or 0
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.ReSpawnAt, { self, Coordinate, Heading }, Delay )
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
else
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, self:GetCountry() )
|
||||
|
||||
SpawnStatic:SpawnFromCoordinate( Coordinate, Heading, self.StaticName )
|
||||
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
|
||||
|
||||
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
|
||||
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -88,8 +88,9 @@
|
||||
--
|
||||
-- @field #UNIT UNIT
|
||||
UNIT = {
|
||||
ClassName="UNIT",
|
||||
UnitName=nil,
|
||||
ClassName="UNIT",
|
||||
UnitName=nil,
|
||||
GroupName=nil,
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +103,7 @@ UNIT = {
|
||||
|
||||
|
||||
-- Registration.
|
||||
|
||||
|
||||
--- Create a new UNIT from DCSUnit.
|
||||
-- @param #UNIT self
|
||||
-- @param #string UnitName The name of the DCS unit.
|
||||
@@ -110,11 +111,20 @@ UNIT = {
|
||||
function UNIT:Register( UnitName )
|
||||
|
||||
-- Inherit CONTROLLABLE.
|
||||
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) )
|
||||
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) --#UNIT
|
||||
|
||||
-- Set unit name.
|
||||
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.
|
||||
self:SetEventPriority( 3 )
|
||||
|
||||
@@ -168,6 +178,29 @@ function UNIT:GetDCSObject()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the unit altitude above sea level in meters.
|
||||
-- @param Wrapper.Unit#UNIT 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 height of the group or nil if is not existing or alive.
|
||||
function UNIT:GetAltitude(FromGround)
|
||||
|
||||
local DCSUnit = Unit.getByName( self.UnitName )
|
||||
|
||||
if DCSUnit then
|
||||
local altitude = 0
|
||||
local point = DCSUnit:getPoint() --DCS#Vec3
|
||||
altitude = point.y
|
||||
if FromGround then
|
||||
local land = land.getHeight( { x = point.x, y = point.z } ) or 0
|
||||
altitude = altitude - land
|
||||
end
|
||||
return altitude
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
|
||||
--
|
||||
-- This function will:
|
||||
@@ -260,6 +293,8 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
_DATABASE:Spawn( SpawnGroupTemplate )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns if the unit is activated.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
|
||||
@@ -296,6 +331,8 @@ function UNIT:IsAlive()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the Unit's callsign - the localized string.
|
||||
-- @param #UNIT self
|
||||
-- @return #string The Callsign of the Unit.
|
||||
@@ -401,6 +438,17 @@ function UNIT:GetClient()
|
||||
return nil
|
||||
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.
|
||||
-- The number is the same number the unit has in ME.
|
||||
-- It may not be changed during the mission.
|
||||
@@ -517,6 +565,63 @@ function UNIT:IsTanker()
|
||||
return tanker, system
|
||||
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.
|
||||
-- @param Wrapper.Unit#UNIT self
|
||||
@@ -544,14 +649,14 @@ end
|
||||
-- @return #string The name of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPrefix()
|
||||
self:F2( self.UnitName )
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 )
|
||||
self:T3( UnitPrefix )
|
||||
return UnitPrefix
|
||||
local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 )
|
||||
self:T3( UnitPrefix )
|
||||
return UnitPrefix
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -676,8 +781,6 @@ function UNIT:GetAmmunition()
|
||||
return nammo, nshells, nrockets, nbombs, nmissiles
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the unit sensors.
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit.Sensors Table of sensors.
|
||||
@@ -954,6 +1057,7 @@ end
|
||||
-- @return #string Some text.
|
||||
function UNIT:GetThreatLevel()
|
||||
|
||||
|
||||
local ThreatLevel = 0
|
||||
local ThreatText = ""
|
||||
|
||||
@@ -979,6 +1083,7 @@ function UNIT:GetThreatLevel()
|
||||
"LR SAMs"
|
||||
}
|
||||
|
||||
|
||||
if Attributes["LR SAM"] then ThreatLevel = 10
|
||||
elseif Attributes["MR SAM"] then ThreatLevel = 9
|
||||
elseif Attributes["SR SAM"] and
|
||||
@@ -992,7 +1097,7 @@ function UNIT:GetThreatLevel()
|
||||
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
|
||||
not Attributes["ATGM"] then ThreatLevel = 3
|
||||
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
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
@@ -1014,6 +1119,7 @@ function UNIT:GetThreatLevel()
|
||||
"Fighter"
|
||||
}
|
||||
|
||||
|
||||
if Attributes["Fighters"] then ThreatLevel = 10
|
||||
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
|
||||
elseif Attributes["Battleplanes"] then ThreatLevel = 8
|
||||
@@ -1111,26 +1217,32 @@ end
|
||||
-- @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.
|
||||
function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
|
||||
self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
|
||||
self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitVec3 = self:GetVec3()
|
||||
local AwaitUnitVec3 = AwaitUnit:GetVec3()
|
||||
local UnitVec3 = self:GetVec3()
|
||||
local AwaitUnitVec3 = AwaitUnit:GetVec3()
|
||||
|
||||
if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then
|
||||
self:T3( "true" )
|
||||
return true
|
||||
else
|
||||
self:T3( "false" )
|
||||
return false
|
||||
end
|
||||
if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then
|
||||
self:T3( "true" )
|
||||
return true
|
||||
else
|
||||
self:T3( "false" )
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Returns if the unit is a friendly unit.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean IsFriendly evaluation result.
|
||||
|
||||
@@ -5,6 +5,7 @@ Utilities/Enums.lua
|
||||
Utilities/Profiler.lua
|
||||
Utilities/Templates.lua
|
||||
Utilities/STTS.lua
|
||||
Utilities/FiFo.lua
|
||||
|
||||
Core/Base.lua
|
||||
Core/Beacon.lua
|
||||
|
||||
@@ -61,9 +61,10 @@ Documentation on the MOOSE class hierarchy, usage guides and background informat
|
||||
|
||||
|
||||
|
||||
## [MOOSE Youtube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg)
|
||||
## [MOOSE Youtube Tutorials](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
|
||||
|
||||
MOOSE has a [broadcast and training channel on YouTube](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) with various channels that you can watch.
|
||||
Pene has kindly created a [tutorial series for MOOSE](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
|
||||
with various videos that you can watch.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user