mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
@@ -657,8 +657,8 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
--- Create a route point of type air.
|
||||
local FromRTBRoutePoint = FromCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
RTBSpeed,
|
||||
true
|
||||
)
|
||||
@@ -666,8 +666,8 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
--- Create a route point of type air.
|
||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
RTBSpeed,
|
||||
true
|
||||
)
|
||||
@@ -761,10 +761,10 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(self.PatrolAltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
--- Create a route point of type air. NOT used!
|
||||
local ToRefuelRoutePoint = Tanker:GetCoordinate():WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
local ToRefuelRoutePoint = Tanker:GetCoordinate():WaypointAir(self.PatrolAltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
|
||||
@@ -453,7 +453,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
@@ -462,7 +462,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
@@ -536,7 +536,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
local EngageRoute = {}
|
||||
local AttackTasks = {}
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
local FromWP = DefenderCoord:WaypointAir(self.EngageAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
@@ -544,7 +544,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
-- TODO: A factor of * 3 this way too low. This causes the AI NOT to engage until very close or even merged sometimes. Some A2A missiles have a much longer range! Needs more frequent updates of the task!
|
||||
|
||||
@@ -309,7 +309,7 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
local speedkmh=ToTargetSpeed
|
||||
|
||||
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = FromWP
|
||||
|
||||
if self.racetrack then
|
||||
@@ -359,9 +359,9 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
else
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = ToWP
|
||||
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
|
||||
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
|
||||
|
||||
@@ -174,8 +174,7 @@ function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
@@ -522,12 +521,12 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
@@ -578,13 +577,13 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -220,16 +220,9 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
|
||||
AIGroup:MessageToRed( "Returning to home base ...", 30 )
|
||||
else
|
||||
-- Okay, we need to send this Group back to the nearest base of the Coalition of the AI.
|
||||
--TODO: i need to rework the POINT_VEC2 thing.
|
||||
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
|
||||
local PointVec2 = COORDINATE:New(AIGroup:GetVec2().x, 0, AIGroup:GetVec2().y)
|
||||
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
|
||||
self:T( ClosestAirbase.AirbaseName )
|
||||
--[[
|
||||
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
|
||||
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
|
||||
AIGroupTemplate.route = RTBRoute
|
||||
AIGroup:Respawn( AIGroupTemplate )
|
||||
]]
|
||||
AIGroup:RouteRTB(ClosestAirbase)
|
||||
end
|
||||
|
||||
|
||||
@@ -423,12 +423,12 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToEngageZoneSpeed,
|
||||
true
|
||||
)
|
||||
@@ -445,13 +445,13 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -162,7 +162,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
@@ -466,12 +465,12 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
@@ -508,13 +507,13 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -440,7 +440,7 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled
|
||||
|
||||
-- To point.
|
||||
local AirbasePointVec2 = Airbase:GetPointVec2()
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(COORDINATE.WaypointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
|
||||
|
||||
--ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||
--ToWaypoint["speed_locked"] = true
|
||||
|
||||
@@ -367,8 +367,8 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
|
||||
-- local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
-- "RADIO",
|
||||
-- POINT_VEC3.RoutePointType.TurningPoint,
|
||||
-- POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
-- COORDINATE.WaypointType.TurningPoint,
|
||||
-- COORDINATE.WaypointAction.TurningPoint,
|
||||
-- Speed,
|
||||
-- true
|
||||
-- )
|
||||
@@ -380,8 +380,8 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
50,
|
||||
true
|
||||
)
|
||||
@@ -427,7 +427,7 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 50, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, 50, true)
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
local Tasks = {}
|
||||
@@ -496,14 +496,14 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,_speed, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint,_speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -563,7 +563,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
@@ -573,7 +573,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -631,7 +631,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
@@ -639,7 +639,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + Height -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
|
||||
@@ -725,7 +725,7 @@ function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, X
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = POINT_VEC3:New()
|
||||
local PointVec3 = COORDINATE:New()
|
||||
PointVec3:SetX( XStart + i * XSpace )
|
||||
PointVec3:SetY( YStart + i * YSpace )
|
||||
PointVec3:SetZ( ZStart + i * ZSpace )
|
||||
@@ -877,7 +877,7 @@ function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event ,
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = POINT_VEC3:New()
|
||||
local PointVec3 = COORDINATE:New()
|
||||
|
||||
local Side = ( i % 2 == 0 ) and 1 or -1
|
||||
local Row = i / 2 + 1
|
||||
@@ -936,7 +936,7 @@ function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XS
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = POINT_VEC3:New()
|
||||
local PointVec3 = COORDINATE:New()
|
||||
|
||||
local ZIndex = i % ZLevels
|
||||
local XIndex = math.floor( i / ZLevels )
|
||||
|
||||
@@ -751,12 +751,12 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
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 CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TakeOffParking,
|
||||
POINT_VEC3.RoutePointAction.FromParkingArea,
|
||||
COORDINATE.WaypointType.TakeOffParking,
|
||||
COORDINATE.WaypointAction.FromParkingArea,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
@@ -767,12 +767,12 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
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 CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
@@ -792,13 +792,13 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
@@ -846,7 +846,6 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
|
||||
RTB = true
|
||||
else
|
||||
end
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
@@ -856,6 +855,16 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
RTB = true
|
||||
end
|
||||
|
||||
if self:IsInstanceOf("AI_CAS") or self:IsInstanceOf("AI_BAI") then
|
||||
local atotal,shells,rockets,bombs,missiles = self.Controllable:GetAmmunition()
|
||||
local arelevant = rockets+bombs
|
||||
if arelevant == 0 or missiles == 0 then
|
||||
RTB = true
|
||||
self:T({total=atotal,shells=shells,rockets=rockets,bombs=bombs,missiles=missiles})
|
||||
self:T( self.Controllable:GetName() .. " is out of ammo, RTB!" )
|
||||
end
|
||||
end
|
||||
|
||||
if RTB == true then
|
||||
self:RTB()
|
||||
else
|
||||
@@ -881,12 +890,12 @@ function AI_PATROL_ZONE:onafterRTB()
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
--local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -275,14 +275,14 @@
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] UnBoard
|
||||
-- @param #CARGO self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
|
||||
--- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier.
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] __UnBoard
|
||||
-- @param #CARGO self
|
||||
-- @param #number DelaySeconds The amount of seconds to delay the action.
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
|
||||
|
||||
-- Load
|
||||
@@ -307,14 +307,14 @@
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] UnLoad
|
||||
-- @param #CARGO self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
|
||||
--- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading.
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] __UnLoad
|
||||
-- @param #CARGO self
|
||||
-- @param #number DelaySeconds The amount of seconds to delay the action.
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
|
||||
-- State Transition Functions
|
||||
|
||||
@@ -467,7 +467,7 @@ do -- CARGO
|
||||
self.Type = Type
|
||||
self.Name = Name
|
||||
self.Weight = Weight or 0
|
||||
self.CargoObject = nil
|
||||
self.CargoObject = nil -- Wrapper.Group#GROUP
|
||||
self.CargoCarrier = nil -- Wrapper.Client#CLIENT
|
||||
self.Representable = false
|
||||
self.Slingloadable = false
|
||||
@@ -897,7 +897,7 @@ do -- CARGO
|
||||
|
||||
--- Get the current PointVec2 of the cargo.
|
||||
-- @param #CARGO self
|
||||
-- @return Core.Point#POINT_VEC2
|
||||
-- @return Core.Point#COORDINATE
|
||||
function CARGO:GetPointVec2()
|
||||
return self.CargoObject:GetPointVec2()
|
||||
end
|
||||
@@ -1094,7 +1094,7 @@ do -- CARGO_REPRESENTABLE
|
||||
|
||||
--- Route a cargo unit to a PointVec2.
|
||||
-- @param #CARGO_REPRESENTABLE self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number Speed
|
||||
-- @return #CARGO_REPRESENTABLE
|
||||
function CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed )
|
||||
|
||||
@@ -114,7 +114,7 @@ do -- CARGO_CRATE
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
-- @param Core.Point#COORDINATE
|
||||
function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 )
|
||||
--self:T( { ToPointVec2, From, Event, To } )
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ do -- CARGO_GROUP
|
||||
--- @type CARGO_GROUP
|
||||
-- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects.
|
||||
-- @field #string GroupName The name of the CargoGroup.
|
||||
-- @field Wrapper.Group#GROUÜ CargoCarrier The carrier group.
|
||||
-- @extends Cargo.Cargo#CARGO_REPORTABLE
|
||||
|
||||
--- Defines a cargo that is represented by a @{Wrapper.Group} object within the simulator.
|
||||
@@ -410,7 +411,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onafterUnBoard( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
self:T( {From, Event, To, ToPointVec2, NearRadius } )
|
||||
@@ -453,7 +454,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
--self:T( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
@@ -491,7 +492,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
||||
--self:T( { From, Event, To, ToPointVec2 } )
|
||||
|
||||
@@ -771,3 +772,4 @@ do -- CARGO_GROUP
|
||||
|
||||
|
||||
end -- CARGO_GROUP
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius (optional) Defaut 25 m.
|
||||
function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
|
||||
self:T( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
@@ -145,7 +145,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius (optional) Defaut 100 m.
|
||||
function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius )
|
||||
self:T( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
@@ -171,7 +171,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius (optional) Defaut 100 m.
|
||||
function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
|
||||
self:T( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
@@ -197,7 +197,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
-- @param Core.Point#COORDINATE
|
||||
function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 )
|
||||
self:T( { ToPointVec2, From, Event, To } )
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ BASE = {
|
||||
States = {},
|
||||
Debug = debug,
|
||||
Scheduler = nil,
|
||||
Properties = {},
|
||||
}
|
||||
|
||||
-- @field #BASE.__
|
||||
@@ -1109,6 +1110,31 @@ function BASE:ClearState( Object, StateName )
|
||||
end
|
||||
end
|
||||
|
||||
--- Set one property of an object.
|
||||
-- @param #BASE self
|
||||
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
|
||||
-- @param Value The value that is stored. Note that the value can be a #string, but it can also be any other type!
|
||||
function BASE:SetProperty(Key,Value)
|
||||
self.Properties = self.Properties or {}
|
||||
self.Properties[Key] = Value
|
||||
end
|
||||
|
||||
--- Get one property of an object by the key.
|
||||
-- @param #BASE self
|
||||
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
|
||||
-- @return Value The value that is stored. Note that the value can be a #string, but it can also be any other type! Nil if not found.
|
||||
function BASE:GetProperty(Key)
|
||||
self.Properties = self.Properties or {}
|
||||
return self.Properties[Key]
|
||||
end
|
||||
|
||||
--- Get all of the properties of an object in a table.
|
||||
-- @param #BASE self
|
||||
-- @return #table of values, indexed by keys.
|
||||
function BASE:GetProperties()
|
||||
return self.Properties
|
||||
end
|
||||
|
||||
-- Trace section
|
||||
|
||||
-- Log a trace (only shown when trace is on)
|
||||
@@ -1440,4 +1466,3 @@ function BASE:I( Arguments )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
--
|
||||
-- @module Core.ClientMenu
|
||||
-- @image Core_Menu.JPG
|
||||
-- last change: May 2024
|
||||
-- last change: Jan 2025
|
||||
|
||||
-- TODO
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
@@ -57,9 +57,9 @@
|
||||
---
|
||||
-- @field #CLIENTMENU
|
||||
CLIENTMENU = {
|
||||
ClassName = "CLIENTMENUE",
|
||||
ClassName = "CLIENTMENU",
|
||||
lid = "",
|
||||
version = "0.1.2",
|
||||
version = "0.1.3",
|
||||
name = nil,
|
||||
path = nil,
|
||||
group = nil,
|
||||
@@ -455,7 +455,7 @@ end
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
-- @return #CLIENTMENUMANAGER self
|
||||
function CLIENTMENUMANAGER:_EventHandler(EventData)
|
||||
function CLIENTMENUMANAGER:_EventHandler(EventData,Retry)
|
||||
self:T(self.lid.."_EventHandler: "..EventData.id)
|
||||
--self:I(self.lid.."_EventHandler: "..tostring(EventData.IniPlayerName))
|
||||
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
|
||||
@@ -468,6 +468,10 @@ function CLIENTMENUMANAGER:_EventHandler(EventData)
|
||||
if EventData.IniPlayerName and EventData.IniGroup then
|
||||
if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniUnitName ))) then
|
||||
self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName)
|
||||
if not Retry then
|
||||
-- try again in 2 secs
|
||||
self:ScheduleOnce(2,CLIENTMENUMANAGER._EventHandler,self,EventData,true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
--self:I(self.lid.."Join event for player: "..EventData.IniPlayerName)
|
||||
@@ -524,7 +528,7 @@ function CLIENTMENUMANAGER:InitAutoPropagation()
|
||||
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
|
||||
self:SetEventPriority(5)
|
||||
self:SetEventPriority(6)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -1375,7 +1375,7 @@ function EVENT:onEvent( Event )
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit.getName and Event.IniDCSUnit:getName() or "Scenery no name "..math.random(1,20000)
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniCategory = Event.IniDCSUnit.getDesc and Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY"
|
||||
|
||||
elseif Event.IniObjectCategory == Object.Category.BASE then
|
||||
|
||||
@@ -948,7 +948,8 @@ do -- FSM
|
||||
end
|
||||
|
||||
do -- FSM_CONTROLLABLE
|
||||
|
||||
|
||||
---
|
||||
-- @type FSM_CONTROLLABLE
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @extends Core.Fsm#FSM
|
||||
@@ -1081,7 +1082,8 @@ do -- FSM_CONTROLLABLE
|
||||
end
|
||||
|
||||
do -- FSM_PROCESS
|
||||
|
||||
|
||||
---
|
||||
-- @type FSM_PROCESS
|
||||
-- @field Tasking.Task#TASK Task
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
@@ -105,6 +105,7 @@ function MENU_INDEX:PrepareCoalition( CoalitionSide )
|
||||
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
|
||||
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
|
||||
end
|
||||
|
||||
---
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
function MENU_INDEX:PrepareGroup( Group )
|
||||
@@ -118,9 +119,11 @@ end
|
||||
function MENU_INDEX:HasMissionMenu( Path )
|
||||
return self.MenuMission.Menus[Path]
|
||||
end
|
||||
|
||||
function MENU_INDEX:SetMissionMenu( Path, Menu )
|
||||
self.MenuMission.Menus[Path] = Menu
|
||||
end
|
||||
|
||||
function MENU_INDEX:ClearMissionMenu( Path )
|
||||
self.MenuMission.Menus[Path] = nil
|
||||
end
|
||||
@@ -128,9 +131,11 @@ end
|
||||
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
||||
return self.Coalition[Coalition].Menus[Path]
|
||||
end
|
||||
|
||||
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
|
||||
self.Coalition[Coalition].Menus[Path] = Menu
|
||||
end
|
||||
|
||||
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
|
||||
self.Coalition[Coalition].Menus[Path] = nil
|
||||
end
|
||||
@@ -138,19 +143,24 @@ end
|
||||
function MENU_INDEX:HasGroupMenu( Group, Path )
|
||||
if Group and Group:IsAlive() then
|
||||
local MenuGroupName = Group:GetName()
|
||||
return self.Group[MenuGroupName].Menus[Path]
|
||||
if self.Group[MenuGroupName] and self.Group[MenuGroupName].Menus and self.Group[MenuGroupName].Menus[Path] then
|
||||
return self.Group[MenuGroupName].Menus[Path]
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
|
||||
local MenuGroupName = Group:GetName()
|
||||
Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
||||
--Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
||||
self.Group[MenuGroupName].Menus[Path] = Menu
|
||||
end
|
||||
|
||||
function MENU_INDEX:ClearGroupMenu( Group, Path )
|
||||
local MenuGroupName = Group:GetName()
|
||||
self.Group[MenuGroupName].Menus[Path] = nil
|
||||
end
|
||||
|
||||
function MENU_INDEX:Refresh( Group )
|
||||
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
|
||||
Menu:Refresh()
|
||||
|
||||
@@ -464,6 +464,7 @@ _MESSAGESRS = {}
|
||||
-- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest).
|
||||
-- @param #string Label (optional) Label, defaults to "MESSAGE" or the Message Category set.
|
||||
-- @param Core.Point#COORDINATE Coordinate (optional) Coordinate this messages originates from.
|
||||
-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC
|
||||
-- @usage
|
||||
-- -- Mind the dot here, not using the colon this time around!
|
||||
-- -- Needed once only
|
||||
@@ -471,7 +472,7 @@ _MESSAGESRS = {}
|
||||
-- -- later on in your code
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||
--
|
||||
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate)
|
||||
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend)
|
||||
|
||||
_MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
|
||||
@@ -489,6 +490,10 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
|
||||
_MESSAGESRS.MSRS:SetCoordinate(Coordinate)
|
||||
end
|
||||
|
||||
if Backend then
|
||||
_MESSAGESRS.MSRS:SetBackend(Backend)
|
||||
end
|
||||
|
||||
_MESSAGESRS.Culture = Culture or MSRS.culture or "en-GB"
|
||||
_MESSAGESRS.MSRS:SetCulture(Culture)
|
||||
|
||||
|
||||
@@ -970,6 +970,9 @@ do -- COORDINATE
|
||||
if not TargetCoordinate then return 1000000 end
|
||||
--local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
|
||||
local a = self:GetVec2()
|
||||
if not TargetCoordinate.ClassName then
|
||||
TargetCoordinate=COORDINATE:NewFromVec3(TargetCoordinate)
|
||||
end
|
||||
local b = TargetCoordinate:GetVec2()
|
||||
local norm=UTILS.VecDist2D(a,b)
|
||||
return norm
|
||||
@@ -1154,6 +1157,162 @@ do -- COORDINATE
|
||||
return vec3
|
||||
end
|
||||
|
||||
--- Return the x coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number The x coordinate.
|
||||
function COORDINATE:GetX()
|
||||
return self.x
|
||||
end
|
||||
|
||||
--- Return the y coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number The y coordinate.
|
||||
function COORDINATE:GetY()
|
||||
if self:IsInstanceOf("POINT_VEC2") then
|
||||
return self.z
|
||||
end
|
||||
return self.y
|
||||
end
|
||||
|
||||
--- Return the z coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number The z coordinate.
|
||||
function COORDINATE:GetZ()
|
||||
return self.z
|
||||
end
|
||||
|
||||
--- Set the x coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number x The x coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:SetX( x )
|
||||
self.x = x
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the y coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number y The y coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:SetY( y )
|
||||
if self:IsInstanceOf("POINT_VEC2") then
|
||||
self.z = y
|
||||
else
|
||||
self.y = y
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the z coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number z The z coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:SetZ( z )
|
||||
self.z = z
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the x coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number x The x coordinate value to add to the current x coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:AddX( x )
|
||||
self.x = self.x + x
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return Return the Lat(itude) coordinate of the COORDINATE (ie: (parent)COORDINATE.x).
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number The x coordinate.
|
||||
function COORDINATE:GetLat()
|
||||
return self.x
|
||||
end
|
||||
|
||||
--- Set the Lat(itude) coordinate of the COORDINATE (ie: COORDINATE.x).
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number x The x coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:SetLat( x )
|
||||
self.x = x
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return the Lon(gitude) coordinate of the COORDINATE (ie: (parent)COORDINATE.z).
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number The y coordinate.
|
||||
function COORDINATE:GetLon()
|
||||
return self.z
|
||||
end
|
||||
|
||||
--- Set the Lon(gitude) coordinate of the COORDINATE (ie: COORDINATE.z).
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number y The y coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:SetLon( z )
|
||||
self.z = z
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return the altitude (height) of the land at the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number The land altitude.
|
||||
function COORDINATE:GetAlt()
|
||||
return self.y ~= 0 or land.getHeight( { x = self.x, y = self.z } )
|
||||
end
|
||||
|
||||
--- Set the altitude of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:SetAlt( Altitude )
|
||||
self.y = Altitude or land.getHeight( { x = self.x, y = self.z } )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the current land height an altitude.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:AddAlt( Altitude )
|
||||
self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return a random COORDINATE within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance OuterRadius
|
||||
-- @param DCS#Distance InnerRadius
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:GetRandomPointVec2InRadius( OuterRadius, InnerRadius )
|
||||
self:F2( { OuterRadius, InnerRadius } )
|
||||
|
||||
return COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
|
||||
end
|
||||
|
||||
--- Add to the y coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number y The y coordinate value to add to the current y coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:AddY( y )
|
||||
if self:IsInstanceOf("POINT_VEC2") then
|
||||
return self:AddZ(y)
|
||||
else
|
||||
self.y = self.y + y
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the z coordinate of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number z The z coordinate value to add to the current z coordinate.
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:AddZ( z )
|
||||
self.z = self.z +z
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Returns a text documenting the wind direction (from) and strength according the measurement system @{Core.Settings}.
|
||||
-- The text will reflect the wind like this:
|
||||
@@ -1218,7 +1377,7 @@ do -- COORDINATE
|
||||
local s = string.format( '%03d°', AngleDegrees )
|
||||
|
||||
if MagVar then
|
||||
local variation = UTILS.GetMagneticDeclination() or 0
|
||||
local variation = self:GetMagneticDeclination() or 0
|
||||
local AngleMagnetic = AngleDegrees - variation
|
||||
|
||||
if AngleMagnetic < 0 then AngleMagnetic = 360-AngleMagnetic end
|
||||
@@ -2956,6 +3115,8 @@ do -- COORDINATE
|
||||
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
|
||||
|
||||
local bearing = UTILS.Round( UTILS.ToDegree( AngleRadians ),0 )
|
||||
local magnetic = self:GetMagneticDeclination() or 0
|
||||
bearing = bearing - magnetic
|
||||
|
||||
local rangeMetres = self:Get2DDistance(currentCoord)
|
||||
local rangeNM = UTILS.Round( UTILS.MetersToNM(rangeMetres), 0)
|
||||
@@ -3469,9 +3630,18 @@ do -- COORDINATE
|
||||
return flat, elev
|
||||
end
|
||||
|
||||
--- Return a random COORDINATE within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance OuterRadius
|
||||
-- @param DCS#Distance InnerRadius
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
|
||||
return COORDINATE:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- POINT_VEC3
|
||||
do
|
||||
|
||||
--- The POINT_VEC3 class
|
||||
-- @type POINT_VEC3
|
||||
@@ -3488,6 +3658,8 @@ do -- POINT_VEC3
|
||||
|
||||
--- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.
|
||||
--
|
||||
-- **DEPRECATED - PLEASE USE COORDINATE!**
|
||||
--
|
||||
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
|
||||
-- In order to keep the credibility of the the author,
|
||||
-- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors,
|
||||
@@ -3575,130 +3747,19 @@ do -- POINT_VEC3
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new POINT_VEC3 object from Vec2 coordinates.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param DCS#Vec2 Vec2 The Vec2 point.
|
||||
-- @param DCS#Distance LandHeightAdd (optional) Add a landheight.
|
||||
-- @return Core.Point#POINT_VEC3 self
|
||||
function POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd )
|
||||
|
||||
local self = BASE:Inherit( self, COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) ) -- Core.Point#POINT_VEC3
|
||||
self:F2( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new POINT_VEC3 object from Vec3 coordinates.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param DCS#Vec3 Vec3 The Vec3 point.
|
||||
-- @return Core.Point#POINT_VEC3 self
|
||||
function POINT_VEC3:NewFromVec3( Vec3 )
|
||||
|
||||
local self = BASE:Inherit( self, COORDINATE:NewFromVec3( Vec3 ) ) -- Core.Point#POINT_VEC3
|
||||
self:F2( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Return the x coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @return #number The x coordinate.
|
||||
function POINT_VEC3:GetX()
|
||||
return self.x
|
||||
end
|
||||
|
||||
--- Return the y coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @return #number The y coordinate.
|
||||
function POINT_VEC3:GetY()
|
||||
return self.y
|
||||
end
|
||||
|
||||
--- Return the z coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @return #number The z coordinate.
|
||||
function POINT_VEC3:GetZ()
|
||||
return self.z
|
||||
end
|
||||
|
||||
--- Set the x coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param #number x The x coordinate.
|
||||
-- @return #POINT_VEC3
|
||||
function POINT_VEC3:SetX( x )
|
||||
self.x = x
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the y coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param #number y The y coordinate.
|
||||
-- @return #POINT_VEC3
|
||||
function POINT_VEC3:SetY( y )
|
||||
self.y = y
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the z coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param #number z The z coordinate.
|
||||
-- @return #POINT_VEC3
|
||||
function POINT_VEC3:SetZ( z )
|
||||
self.z = z
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the x coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param #number x The x coordinate value to add to the current x coordinate.
|
||||
-- @return #POINT_VEC3
|
||||
function POINT_VEC3:AddX( x )
|
||||
self.x = self.x + x
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the y coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param #number y The y coordinate value to add to the current y coordinate.
|
||||
-- @return #POINT_VEC3
|
||||
function POINT_VEC3:AddY( y )
|
||||
self.y = self.y + y
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the z coordinate of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param #number z The z coordinate value to add to the current z coordinate.
|
||||
-- @return #POINT_VEC3
|
||||
function POINT_VEC3:AddZ( z )
|
||||
self.z = self.z +z
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return a random POINT_VEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param DCS#Distance OuterRadius
|
||||
-- @param DCS#Distance InnerRadius
|
||||
-- @return #POINT_VEC3
|
||||
function POINT_VEC3:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
|
||||
|
||||
return POINT_VEC3:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- POINT_VEC2
|
||||
do
|
||||
|
||||
-- @type POINT_VEC2
|
||||
--- @type POINT_VEC2
|
||||
-- @field DCS#Distance x The x coordinate in meters.
|
||||
-- @field DCS#Distance y the y coordinate in meters.
|
||||
-- @extends Core.Point#COORDINATE
|
||||
|
||||
--- Defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
|
||||
--
|
||||
-- **DEPRECATED - PLEASE USE COORDINATE!**
|
||||
--
|
||||
-- ## POINT_VEC2 constructor
|
||||
--
|
||||
-- A new POINT_VEC2 instance can be created with:
|
||||
@@ -3746,166 +3807,4 @@ do -- POINT_VEC2
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new POINT_VEC2 object from Vec2 coordinates.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param DCS#Vec2 Vec2 The Vec2 point.
|
||||
-- @return Core.Point#POINT_VEC2 self
|
||||
function POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd )
|
||||
|
||||
local LandHeight = land.getHeight( Vec2 )
|
||||
|
||||
LandHeightAdd = LandHeightAdd or 0
|
||||
LandHeight = LandHeight + LandHeightAdd
|
||||
|
||||
local self = BASE:Inherit( self, COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) ) -- #POINT_VEC2
|
||||
self:F2( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new POINT_VEC2 object from Vec3 coordinates.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param DCS#Vec3 Vec3 The Vec3 point.
|
||||
-- @return Core.Point#POINT_VEC2 self
|
||||
function POINT_VEC2:NewFromVec3( Vec3 )
|
||||
|
||||
local self = BASE:Inherit( self, COORDINATE:NewFromVec3( Vec3 ) ) -- #POINT_VEC2
|
||||
self:F2( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return the x coordinate of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @return #number The x coordinate.
|
||||
function POINT_VEC2:GetX()
|
||||
return self.x
|
||||
end
|
||||
|
||||
--- Return the y coordinate of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @return #number The y coordinate.
|
||||
function POINT_VEC2:GetY()
|
||||
return self.z
|
||||
end
|
||||
|
||||
--- Set the x coordinate of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number x The x coordinate.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:SetX( x )
|
||||
self.x = x
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the y coordinate of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number y The y coordinate.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:SetY( y )
|
||||
self.z = y
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return Return the Lat(itude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.x).
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @return #number The x coordinate.
|
||||
function POINT_VEC2:GetLat()
|
||||
return self.x
|
||||
end
|
||||
|
||||
--- Set the Lat(itude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.x).
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number x The x coordinate.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:SetLat( x )
|
||||
self.x = x
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return the Lon(gitude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.z).
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @return #number The y coordinate.
|
||||
function POINT_VEC2:GetLon()
|
||||
return self.z
|
||||
end
|
||||
|
||||
--- Set the Lon(gitude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.z).
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number y The y coordinate.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:SetLon( z )
|
||||
self.z = z
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return the altitude (height) of the land at the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @return #number The land altitude.
|
||||
function POINT_VEC2:GetAlt()
|
||||
return self.y ~= 0 or land.getHeight( { x = self.x, y = self.z } )
|
||||
end
|
||||
|
||||
--- Set the altitude of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:SetAlt( Altitude )
|
||||
self.y = Altitude or land.getHeight( { x = self.x, y = self.z } )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the x coordinate of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number x The x coordinate.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:AddX( x )
|
||||
self.x = self.x + x
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the y coordinate of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number y The y coordinate.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:AddY( y )
|
||||
self.z = self.z + y
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add to the current land height an altitude.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set.
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:AddAlt( Altitude )
|
||||
self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return a random POINT_VEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC2.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param DCS#Distance OuterRadius
|
||||
-- @param DCS#Distance InnerRadius
|
||||
-- @return #POINT_VEC2
|
||||
function POINT_VEC2:GetRandomPointVec2InRadius( OuterRadius, InnerRadius )
|
||||
self:F2( { OuterRadius, InnerRadius } )
|
||||
|
||||
return POINT_VEC2:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
|
||||
end
|
||||
|
||||
-- TODO: Check this to replace
|
||||
--- Calculate the distance from a reference @{#POINT_VEC2}.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param #POINT_VEC2 PointVec2Reference The reference @{#POINT_VEC2}.
|
||||
-- @return DCS#Distance The distance from the reference @{#POINT_VEC2} in meters.
|
||||
function POINT_VEC2:DistanceFromPointVec2( PointVec2Reference )
|
||||
self:F2( PointVec2Reference )
|
||||
|
||||
local Distance = ( ( PointVec2Reference.x - self.x ) ^ 2 + ( PointVec2Reference.z - self.z ) ^2 ) ^ 0.5
|
||||
|
||||
self:T2( Distance )
|
||||
return Distance
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -289,7 +289,14 @@ do -- SET_BASE
|
||||
|
||||
-- Debug info.
|
||||
--self:T2( { ObjectName = ObjectName, Object = Object } )
|
||||
|
||||
|
||||
-- Error ahndling
|
||||
if not ObjectName or ObjectName == "" then
|
||||
self:E("SET_BASE:Add - Invalid ObjectName handed")
|
||||
self:E({ObjectName=ObjectName, Object=Object})
|
||||
return self
|
||||
end
|
||||
|
||||
-- Ensure that the existing element is removed from the Set before a new one is inserted to the Set
|
||||
if self.Set[ObjectName] then
|
||||
self:Remove( ObjectName, true )
|
||||
@@ -524,6 +531,21 @@ do -- SET_BASE
|
||||
|
||||
return self.SomeIteratorLimit or self:Count()
|
||||
end
|
||||
|
||||
--- Get max threat level of all objects in the SET.
|
||||
-- @param #SET_BASE self
|
||||
-- @return #number Max threat level found.
|
||||
function SET_BASE:GetThreatLevelMax()
|
||||
local ThreatMax = 0
|
||||
for _,_unit in pairs(self.Set or {}) do
|
||||
local unit = _unit -- Wrapper.Unit#UNIT
|
||||
local threat = unit.GetThreatLevel and unit:GetThreatLevel() or 0
|
||||
if threat > ThreatMax then
|
||||
ThreatMax = threat
|
||||
end
|
||||
end
|
||||
return ThreatMax
|
||||
end
|
||||
|
||||
--- Filters for the defined collection.
|
||||
-- @param #SET_BASE self
|
||||
@@ -607,14 +629,14 @@ do -- SET_BASE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#POINT_VEC2}.
|
||||
--- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#COORDINATE}.
|
||||
-- @param #SET_BASE self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#COORDINATE} or @{Core.Point#POINT_VEC2} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set.
|
||||
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set.
|
||||
-- @return Core.Base#BASE The closest object.
|
||||
-- @usage
|
||||
-- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() )
|
||||
function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 )
|
||||
--self:F2( PointVec2 )
|
||||
function SET_BASE:FindNearestObjectFromPointVec2( Coordinate )
|
||||
--self:F2( Coordinate )
|
||||
|
||||
local NearestObject = nil
|
||||
local ClosestDistance = nil
|
||||
@@ -622,9 +644,9 @@ do -- SET_BASE
|
||||
for ObjectID, ObjectData in pairs( self.Set ) do
|
||||
if NearestObject == nil then
|
||||
NearestObject = ObjectData
|
||||
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
else
|
||||
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
if Distance < ClosestDistance then
|
||||
NearestObject = ObjectData
|
||||
ClosestDistance = Distance
|
||||
@@ -1220,12 +1242,12 @@ do
|
||||
return GroupFound
|
||||
end
|
||||
|
||||
--- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#POINT_VEC2}.
|
||||
--- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#COORDINATE}.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set.
|
||||
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest object in the set.
|
||||
-- @return Wrapper.Group#GROUP The closest group.
|
||||
function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 )
|
||||
--self:F2( PointVec2 )
|
||||
function SET_GROUP:FindNearestGroupFromPointVec2( Coordinate )
|
||||
--self:F2( Coordinate )
|
||||
|
||||
local NearestGroup = nil -- Wrapper.Group#GROUP
|
||||
local ClosestDistance = nil
|
||||
@@ -1235,9 +1257,9 @@ do
|
||||
for ObjectID, ObjectData in pairs( Set ) do
|
||||
if NearestGroup == nil then
|
||||
NearestGroup = ObjectData
|
||||
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
else
|
||||
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
if Distance < ClosestDistance then
|
||||
NearestGroup = ObjectData
|
||||
ClosestDistance = Distance
|
||||
@@ -1538,6 +1560,13 @@ do
|
||||
local size = 1
|
||||
if Event.IniDCSGroup then
|
||||
size = Event.IniDCSGroup:getSize()
|
||||
elseif Event.IniDCSGroupName then
|
||||
local grp = Group.getByName(Event.IniDCSGroupName)
|
||||
if grp then
|
||||
size = grp:getSize()
|
||||
end
|
||||
elseif Object:IsAlive() then
|
||||
size = Object:CountAliveUnits()
|
||||
end
|
||||
if size == 1 then -- Only remove if the last unit of the group was destroyed.
|
||||
self:Remove( ObjectName )
|
||||
@@ -2490,6 +2519,35 @@ do -- SET_UNIT
|
||||
)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of units which belong to groups with certain **group names**.
|
||||
-- @param #SET_UNIT self
|
||||
-- @param #string Prefixes The (partial) group names to look for. Can be a single string or a table of strings.
|
||||
-- @return #SET_UNIT self
|
||||
function SET_UNIT:FilterGroupPrefixes(Prefixes)
|
||||
if type(Prefixes) == "string" then
|
||||
Prefixes = {Prefixes}
|
||||
end
|
||||
self:FilterFunction(
|
||||
function(unit,prefixes)
|
||||
local outcome = false
|
||||
if unit then
|
||||
local grp = unit:GetGroup()
|
||||
local gname = grp ~= nil and grp:GetName() or "none"
|
||||
for _,_fix in pairs(prefixes or {}) do
|
||||
if string.find(gname,_fix) then
|
||||
outcome = true
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
return outcome
|
||||
end, Prefixes
|
||||
)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of units having a radar of give types.
|
||||
-- All the units having a radar of a given type will be included within the set.
|
||||
@@ -4405,6 +4463,35 @@ do -- SET_CLIENT
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of clients which belong to groups with certain **group names**.
|
||||
-- @param #SET_CLIENT self
|
||||
-- @param #string Prefixes The (partial) group names to look for. Can be anywhere in the group name. Can be a single string or a table of strings.
|
||||
-- @return #SET_CLIENT self
|
||||
function SET_CLIENT:FilterGroupPrefixes(Prefixes)
|
||||
if type(Prefixes) == "string" then
|
||||
Prefixes = {Prefixes}
|
||||
end
|
||||
self:FilterFunction(
|
||||
function(unit,prefixes)
|
||||
local outcome = false
|
||||
if unit then
|
||||
local grp = unit:GetGroup()
|
||||
local gname = grp ~= nil and grp:GetName() or "none"
|
||||
for _,_fix in pairs(prefixes or {}) do
|
||||
if string.find(gname,_fix) then
|
||||
outcome = true
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
return outcome
|
||||
end, Prefixes
|
||||
)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of clients that are only active.
|
||||
-- Only the clients that are active will be included within the set.
|
||||
@@ -4582,6 +4669,16 @@ do -- SET_CLIENT
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Make the SET handle CA slots **only** (GROUND units used by any player). Needs active filtering with `FilterStart()`
|
||||
-- @param #SET_CLIENT self
|
||||
-- @return #SET_CLIENT self
|
||||
function SET_CLIENT:HandleCASlots()
|
||||
self:HandleEvent(EVENTS.PlayerEnterUnit,SET_CLIENT._EventPlayerEnterUnit)
|
||||
self:HandleEvent(EVENTS.PlayerLeaveUnit,SET_CLIENT._EventPlayerLeaveUnit)
|
||||
self:FilterFunction(function(client) if client and client:IsAlive() and client:IsGround() then return true else return false end end)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||
@@ -5364,6 +5461,7 @@ do -- SET_AIRBASE
|
||||
Airbases = {},
|
||||
Filter = {
|
||||
Coalitions = nil,
|
||||
Zones = nil,
|
||||
},
|
||||
FilterMeta = {
|
||||
Coalitions = {
|
||||
@@ -5515,6 +5613,31 @@ do -- SET_AIRBASE
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of airbase objects in zones.
|
||||
-- @param #SET_AIRBASE self
|
||||
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
|
||||
-- @return #SET_AIRBASE self
|
||||
function SET_AIRBASE:FilterZones( Zones )
|
||||
if not self.Filter.Zones then
|
||||
self.Filter.Zones = {}
|
||||
end
|
||||
local zones = {}
|
||||
if Zones.ClassName and Zones.ClassName == "SET_ZONE" then
|
||||
zones = Zones.Set
|
||||
elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then
|
||||
self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!")
|
||||
return self
|
||||
else
|
||||
zones = Zones
|
||||
end
|
||||
for _,Zone in pairs( zones ) do
|
||||
local zonename = Zone:GetName()
|
||||
--self:T((zonename)
|
||||
self.Filter.Zones[zonename] = Zone
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Starts the filtering.
|
||||
-- @param #SET_AIRBASE self
|
||||
@@ -5605,14 +5728,14 @@ do -- SET_AIRBASE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#POINT_VEC2}.
|
||||
--- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#COORDINATE}.
|
||||
-- @param #SET_AIRBASE self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}.
|
||||
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}.
|
||||
-- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}.
|
||||
function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 )
|
||||
--self:F2( PointVec2 )
|
||||
function SET_AIRBASE:FindNearestAirbaseFromPointVec2( Coordinate )
|
||||
--self:F2( Coordinate )
|
||||
|
||||
local NearestAirbase = self:FindNearestObjectFromPointVec2( PointVec2 )
|
||||
local NearestAirbase = self:FindNearestObjectFromPointVec2( Coordinate )
|
||||
return NearestAirbase
|
||||
end
|
||||
|
||||
@@ -5653,6 +5776,20 @@ do -- SET_AIRBASE
|
||||
--self:T(( { "Evaluated Category", MAirbaseCategory } )
|
||||
MAirbaseInclude = MAirbaseInclude and MAirbaseCategory
|
||||
end
|
||||
|
||||
if self.Filter.Zones and MAirbaseInclude then
|
||||
local MAirbaseZone = false
|
||||
for ZoneName, Zone in pairs( self.Filter.Zones ) do
|
||||
--self:T(( "Zone:", ZoneName )
|
||||
local coord = MAirbase:GetCoordinate()
|
||||
if coord and Zone:IsCoordinateInZone(coord) then
|
||||
MAirbaseZone = true
|
||||
end
|
||||
--self:T(( { "Evaluated Zone", MSceneryZone } )
|
||||
end
|
||||
MAirbaseInclude = MAirbaseInclude and MAirbaseZone
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if self.Filter.Functions and MAirbaseInclude then
|
||||
@@ -5928,17 +6065,19 @@ do -- SET_CARGO
|
||||
return self
|
||||
end
|
||||
|
||||
--- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#POINT_VEC2}.
|
||||
--- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#COORDINATE}.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Cargo.Cargo#CARGO}.
|
||||
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Cargo.Cargo#CARGO}.
|
||||
-- @return Cargo.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}.
|
||||
function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) -- R2.1
|
||||
--self:F2( PointVec2 )
|
||||
function SET_CARGO:FindNearestCargoFromPointVec2( Coordinate ) -- R2.1
|
||||
--self:F2( Coordinate )
|
||||
|
||||
local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 )
|
||||
local NearestCargo = self:FindNearestObjectFromPointVec2( Coordinate )
|
||||
return NearestCargo
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #SET_CARGO self
|
||||
function SET_CARGO:FirstCargoWithState( State )
|
||||
|
||||
local FirstCargo = nil
|
||||
@@ -5953,6 +6092,8 @@ do -- SET_CARGO
|
||||
return FirstCargo
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #SET_CARGO self
|
||||
function SET_CARGO:FirstCargoWithStateAndNotDeployed( State )
|
||||
|
||||
local FirstCargo = nil
|
||||
@@ -7971,7 +8112,7 @@ function SET_OPSGROUP:_EventOnBirth(Event)
|
||||
function SET_OPSGROUP:_EventOnDeadOrCrash( Event )
|
||||
--self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniDCSGroup 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.
|
||||
|
||||
@@ -1081,7 +1081,7 @@ function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
|
||||
self.SpawnRandomizeTemplate = true
|
||||
|
||||
for SpawnGroupID = 1, self.SpawnMaxGroups do
|
||||
self:_RandomizeTemplate( SpawnGroupID )
|
||||
self:_RandomizeTemplate( SpawnGroupID, RandomizePositionInZone )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -1093,6 +1093,7 @@ end
|
||||
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Set#SET_GROUP SpawnTemplateSet A SET_GROUP object set, that contains the groups that are possible unit representatives of the group to be spawned.
|
||||
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
|
||||
-- @return #SPAWN
|
||||
-- @usage
|
||||
--
|
||||
@@ -1111,11 +1112,11 @@ end
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
--
|
||||
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet )
|
||||
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet,RandomizePositionInZone )
|
||||
--self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
local setnames = SpawnTemplateSet:GetSetNames()
|
||||
self:InitRandomizeTemplate(setnames)
|
||||
self:InitRandomizeTemplate(setnames,RandomizePositionInZone)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1125,7 +1126,8 @@ end
|
||||
-- but they will all follow the same Template route and have the same prefix name.
|
||||
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
|
||||
-- @param #SPAWN self
|
||||
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
|
||||
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
|
||||
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
|
||||
-- @return #SPAWN
|
||||
-- @usage
|
||||
--
|
||||
@@ -1141,12 +1143,12 @@ end
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
--
|
||||
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) -- R2.3
|
||||
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes, RandomizePositionInZone ) -- R2.3
|
||||
--self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
local SpawnTemplateSet = SET_GROUP:New():FilterPrefixes( SpawnTemplatePrefixes ):FilterOnce()
|
||||
|
||||
self:InitRandomizeTemplateSet( SpawnTemplateSet )
|
||||
self:InitRandomizeTemplateSet( SpawnTemplateSet, RandomizePositionInZone )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1166,6 +1168,7 @@ end
|
||||
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
|
||||
-- @param #SPAWN self
|
||||
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
|
||||
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
--
|
||||
@@ -1178,7 +1181,7 @@ end
|
||||
-- :InitRandomizeZones( ZoneTable )
|
||||
-- :SpawnScheduled( 5, .5 )
|
||||
--
|
||||
function SPAWN:InitRandomizeZones( SpawnZoneTable )
|
||||
function SPAWN:InitRandomizeZones( SpawnZoneTable, RandomizePositionInZone )
|
||||
--self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } )
|
||||
|
||||
local temptable = {}
|
||||
@@ -1190,7 +1193,7 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable )
|
||||
self.SpawnRandomizeZones = true
|
||||
|
||||
for SpawnGroupID = 1, self.SpawnMaxGroups do
|
||||
self:_RandomizeZones( SpawnGroupID )
|
||||
self:_RandomizeZones( SpawnGroupID, RandomizePositionInZone )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -1275,6 +1278,7 @@ end
|
||||
|
||||
--- Respawn group after landing.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number WaitingTime Wait this many seconds before despawning the alive group after landing. Defaults to 3 .
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
--
|
||||
@@ -1282,15 +1286,16 @@ end
|
||||
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
|
||||
-- SpawnRU_SU34 = SPAWN:New( 'Su-34' )
|
||||
-- :InitRandomizeRoute( 1, 1, 3000 )
|
||||
-- :InitRepeatOnLanding()
|
||||
-- :InitRepeatOnLanding(20)
|
||||
-- :Spawn()
|
||||
--
|
||||
function SPAWN:InitRepeatOnLanding()
|
||||
function SPAWN:InitRepeatOnLanding(WaitingTime)
|
||||
--self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
self:InitRepeat()
|
||||
self.RepeatOnEngineShutDown = false
|
||||
self.RepeatOnLanding = true
|
||||
self.RepeatOnLandingTime = (WaitingTime and WaitingTime > 3) and WaitingTime or 3
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1626,7 +1631,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
|
||||
if SpawnTemplate then
|
||||
|
||||
local PointVec3 = POINT_VEC3:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y )
|
||||
local PointVec3 = COORDINATE:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y )
|
||||
--self:T2( { "Current point of ", self.SpawnTemplatePrefix, PointVec3 } )
|
||||
|
||||
-- If RandomizePosition, then Randomize the formation in the zone band, keeping the template.
|
||||
@@ -2028,12 +2033,10 @@ end
|
||||
--
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig )
|
||||
--
|
||||
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn, Parkingdata ) -- R2.2, R2.4
|
||||
--self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } )
|
||||
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn, Parkingdata )
|
||||
|
||||
-- Get position of airbase.
|
||||
local PointVec3 = SpawnAirbase:GetCoordinate()
|
||||
--self:T2( PointVec3 )
|
||||
|
||||
-- Set take off type. Default is hot.
|
||||
Takeoff = Takeoff or SPAWN.Takeoff.Hot
|
||||
@@ -2043,39 +2046,24 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
EmergencyAirSpawn = true
|
||||
end
|
||||
|
||||
--self:F( { SpawnIndex = self.SpawnIndex } )
|
||||
|
||||
if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then
|
||||
|
||||
-- Get group template.
|
||||
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
|
||||
|
||||
--self:F( { SpawnTemplate = SpawnTemplate } )
|
||||
|
||||
if SpawnTemplate then
|
||||
|
||||
-- Check if the aircraft with the specified SpawnIndex is already spawned.
|
||||
-- If yes, ensure that the aircraft is spawned at the same aircraft spot.
|
||||
|
||||
local GroupAlive = self:GetGroupFromIndex( self.SpawnIndex )
|
||||
|
||||
--self:F( { GroupAlive = GroupAlive } )
|
||||
|
||||
-- Debug output
|
||||
--self:T2( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } )
|
||||
|
||||
-- Template group, unit and its attributes.
|
||||
local TemplateGroup = GROUP:FindByName( self.SpawnTemplatePrefix )
|
||||
local TemplateUnit = TemplateGroup:GetUnit( 1 )
|
||||
local group = GROUP:FindByName( self.SpawnTemplatePrefix )
|
||||
local unit = group:GetUnit( 1 )
|
||||
|
||||
-- General category of spawned group.
|
||||
local group = TemplateGroup
|
||||
local istransport = group:HasAttribute( "Transports" ) and group:HasAttribute( "Planes" )
|
||||
local isawacs = group:HasAttribute( "AWACS" )
|
||||
local isfighter = group:HasAttribute( "Fighters" ) or group:HasAttribute( "Interceptors" ) or group:HasAttribute( "Multirole fighters" ) or (group:HasAttribute( "Bombers" ) and not group:HasAttribute( "Strategic bombers" ))
|
||||
local isbomber = group:HasAttribute( "Strategic bombers" )
|
||||
local istanker = group:HasAttribute( "Tankers" )
|
||||
local ishelo = TemplateUnit:HasAttribute( "Helicopters" )
|
||||
local ishelo = unit:HasAttribute( "Helicopters" )
|
||||
|
||||
-- Number of units in the group. With grouping this can actually differ from the template group size!
|
||||
local nunits = #SpawnTemplate.units
|
||||
@@ -2093,40 +2081,32 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
local AirbaseCategory = SpawnAirbase:GetAirbaseCategory()
|
||||
--self:F( { AirbaseCategory = AirbaseCategory } )
|
||||
|
||||
-- Set airdromeId.
|
||||
-- Set airdrome ID. For helipads and ships we need to add the helipad ID and linked unit.
|
||||
-- Note, it is important not to set the airdrome ID for at least ships, because spawn will happen at origin of the map
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
SpawnPoint.linkUnit = AirbaseID
|
||||
SpawnPoint.helipadId = AirbaseID
|
||||
elseif AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
SpawnPoint.linkUnit = AirbaseID
|
||||
SpawnPoint.helipadId = AirbaseID
|
||||
elseif AirbaseCategory == Airbase.Category.AIRDROME then
|
||||
else
|
||||
SpawnPoint.airdromeId = AirbaseID
|
||||
end
|
||||
|
||||
-- Set waypoint type/action.
|
||||
SpawnPoint.alt = 0
|
||||
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
|
||||
SpawnPoint.alt = 0
|
||||
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
|
||||
SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
|
||||
|
||||
-- Check if we spawn on ground.
|
||||
local spawnonground = not (Takeoff == SPAWN.Takeoff.Air)
|
||||
--self:T2( { spawnonground = spawnonground, TOtype = Takeoff, TOair = Takeoff == SPAWN.Takeoff.Air } )
|
||||
|
||||
-- Check where we actually spawn if we spawn on ground.
|
||||
local spawnonship = false
|
||||
local spawnonfarp = false
|
||||
local spawnonrunway = false
|
||||
local spawnonairport = false
|
||||
if spawnonground then
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
spawnonship = true
|
||||
elseif AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
spawnonfarp = true
|
||||
elseif AirbaseCategory == Airbase.Category.AIRDROME then
|
||||
spawnonairport = true
|
||||
end
|
||||
spawnonrunway = Takeoff == SPAWN.Takeoff.Runway
|
||||
local autoparking=false
|
||||
if SpawnAirbase.isAirdrome then
|
||||
autoparking=false
|
||||
else
|
||||
autoparking=true
|
||||
end
|
||||
|
||||
-- Array with parking spots coordinates.
|
||||
@@ -2142,8 +2122,8 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
-- Set terminal type.
|
||||
local termtype = TerminalType
|
||||
if spawnonrunway then
|
||||
if spawnonship then
|
||||
if Takeoff==SPAWN.Takeoff.Runway then
|
||||
if SpawnAirbase.isShip then
|
||||
-- Looks like there are no runway spawn spots on the stennis!
|
||||
if ishelo then
|
||||
termtype = AIRBASE.TerminalType.HelicopterUsable
|
||||
@@ -2163,34 +2143,31 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
local verysafe = false
|
||||
|
||||
-- Number of free parking spots at the airbase.
|
||||
if spawnonship or spawnonfarp or spawnonrunway then
|
||||
if autoparking then
|
||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||
--self:T2( string.format( "Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||
--[[
|
||||
elseif Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
-- Parking data explicitly set by user as input parameter. (This was commented out for some unknown reason. But I need it this way.)
|
||||
nfree=#Parkingdata
|
||||
spots=Parkingdata
|
||||
]]
|
||||
else
|
||||
if ishelo then
|
||||
if termtype == nil then
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
nfree = #spots
|
||||
if nfree < nunits then
|
||||
-- Not enough helo ports. Let's try also other terminal types.
|
||||
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
nfree = #spots
|
||||
end
|
||||
else
|
||||
-- No terminal type specified. We try all spots except shelters.
|
||||
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
nfree = #spots
|
||||
end
|
||||
else
|
||||
@@ -2199,44 +2176,33 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
if isbomber or istransport or istanker or isawacs then
|
||||
-- First we fill the potentially bigger spots.
|
||||
--self:T2( string.format( "Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
nfree = #spots
|
||||
if nfree < nunits then
|
||||
-- Now we try the smaller ones.
|
||||
--self:T2( string.format( "Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
nfree = #spots
|
||||
end
|
||||
else
|
||||
--self:T2( string.format( "Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
nfree = #spots
|
||||
end
|
||||
else
|
||||
-- Terminal type explicitly given.
|
||||
--self:T2( string.format( "Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring( termtype ) ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||
nfree = #spots
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Debug: Get parking data.
|
||||
--[[
|
||||
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
|
||||
--self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
--self:T2(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d",
|
||||
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
|
||||
end
|
||||
--self:T2(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
|
||||
]]
|
||||
|
||||
-- Set this to true if not enough spots are available for emergency air start.
|
||||
local _notenough = false
|
||||
|
||||
-- Need to differentiate some cases again.
|
||||
if spawnonship or spawnonfarp or spawnonrunway then
|
||||
if autoparking then
|
||||
|
||||
-- On free spot required in these cases.
|
||||
if nfree >= 1 then
|
||||
@@ -2254,7 +2220,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
_notenough = true
|
||||
end
|
||||
|
||||
elseif spawnonairport then
|
||||
else
|
||||
|
||||
if nfree >= nunits then
|
||||
|
||||
@@ -2276,13 +2242,10 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
self:E( string.format( "WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
|
||||
-- Not enough parking spots at the airport ==> Spawn in air.
|
||||
spawnonground = false
|
||||
spawnonship = false
|
||||
spawnonfarp = false
|
||||
spawnonrunway = false
|
||||
autoparking=false
|
||||
|
||||
-- Set waypoint type/action to turning point.
|
||||
SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point
|
||||
SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point
|
||||
SpawnPoint.action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point
|
||||
|
||||
-- Adjust altitude to be 500-1000 m above the airbase.
|
||||
@@ -2324,7 +2287,6 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
SpawnTemplate.parked = true
|
||||
|
||||
for UnitID = 1, nunits do
|
||||
--self:T2( 'Before Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
|
||||
-- Template of the current unit.
|
||||
local UnitTemplate = SpawnTemplate.units[UnitID]
|
||||
@@ -2340,9 +2302,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
if spawnonground then
|
||||
|
||||
-- Ships and FARPS seem to have a build in queue.
|
||||
if spawnonship or spawnonfarp or spawnonrunway then
|
||||
|
||||
--self:T2( string.format( "Group %s spawning at farp, ship or runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
if autoparking then
|
||||
|
||||
-- Spawn on ship. We take only the position of the ship.
|
||||
SpawnTemplate.units[UnitID].x = PointVec3.x -- TX
|
||||
@@ -2351,20 +2311,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
else
|
||||
|
||||
--self:T2( string.format( "Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID] ) )
|
||||
|
||||
-- Get coordinates of parking spot.
|
||||
SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x
|
||||
SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z
|
||||
SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y
|
||||
|
||||
-- parkingspots[UnitID]:MarkToAll(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID]))
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
--self:T2( string.format( "Group %s spawning in air at %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
|
||||
-- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set.
|
||||
SpawnTemplate.units[UnitID].x = TX
|
||||
SpawnTemplate.units[UnitID].y = TY
|
||||
@@ -2378,11 +2333,6 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
if parkingindex[UnitID] then
|
||||
UnitTemplate.parking = parkingindex[UnitID]
|
||||
end
|
||||
|
||||
-- Debug output.
|
||||
--self:T2( string.format( "Group %s unit number %d: Parking = %s", self.SpawnTemplatePrefix, UnitID, tostring( UnitTemplate.parking ) ) )
|
||||
--self:T2( string.format( "Group %s unit number %d: Parking ID = %s", self.SpawnTemplatePrefix, UnitID, tostring( UnitTemplate.parking_id ) ) )
|
||||
--self:T2( 'After Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2402,14 +2352,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
-- When spawned in the air, we need to generate a Takeoff Event.
|
||||
if Takeoff == GROUP.Takeoff.Air then
|
||||
for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do
|
||||
SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() }, 5 )
|
||||
--SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() }, 5 ) --No need to create a new SCHEDULER instance every time!
|
||||
self:ScheduleOnce(5, BASE.CreateEventTakeoff, {GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject()})
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if we accidentally spawned on the runway. Needs to be schedules, because group is not immidiately alive.
|
||||
if Takeoff ~= SPAWN.Takeoff.Runway and Takeoff ~= SPAWN.Takeoff.Air and spawnonairport then
|
||||
SCHEDULER:New( nil, AIRBASE.CheckOnRunWay, { SpawnAirbase, GroupSpawned, 75, true }, 1.0 )
|
||||
end
|
||||
--if Takeoff ~= SPAWN.Takeoff.Runway and Takeoff ~= SPAWN.Takeoff.Air and spawnonairport then
|
||||
-- SCHEDULER:New( nil, AIRBASE.CheckOnRunWay, { SpawnAirbase, GroupSpawned, 75, true }, 1.0 )
|
||||
--end
|
||||
|
||||
return GroupSpawned
|
||||
end
|
||||
@@ -2879,7 +2830,7 @@ end
|
||||
function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
|
||||
--self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } )
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
|
||||
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
|
||||
--self:T2( PointVec3 )
|
||||
|
||||
if SpawnIndex then
|
||||
@@ -2955,7 +2906,7 @@ end
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group.
|
||||
-- @param Core.Point#COORDINATE PointVec3 The COORDINATE coordinates where to spawn the group.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
-- @usage
|
||||
@@ -3003,12 +2954,12 @@ function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
|
||||
return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air.
|
||||
end
|
||||
|
||||
--- Will spawn a group from a POINT_VEC2 in 3D space.
|
||||
--- Will spawn a group from a COORDINATE in 3D space.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 coordinates where to spawn the group.
|
||||
-- @param Core.Point#COORDINATE PointVec2 The coordinates where to spawn the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
@@ -3814,8 +3765,9 @@ end
|
||||
--- Private method that randomizes the @{Core.Zone}s where the Group will be spawned.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number SpawnIndex
|
||||
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
|
||||
-- @return #SPAWN self
|
||||
function SPAWN:_RandomizeZones( SpawnIndex )
|
||||
function SPAWN:_RandomizeZones( SpawnIndex, RandomizePositionInZone)
|
||||
--self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeZones } )
|
||||
|
||||
if self.SpawnRandomizeZones then
|
||||
@@ -3829,7 +3781,11 @@ function SPAWN:_RandomizeZones( SpawnIndex )
|
||||
|
||||
--self:T2( "Preparing Spawn in Zone", SpawnZone:GetName() )
|
||||
|
||||
local SpawnVec2 = SpawnZone:GetRandomVec2()
|
||||
local SpawnVec2 = SpawnZone:GetVec2()
|
||||
|
||||
if RandomizePositionInZone ~= false then
|
||||
SpawnVec2 = SpawnZone:GetRandomVec2()
|
||||
end
|
||||
|
||||
--self:T2( { SpawnVec2 = SpawnVec2 } )
|
||||
|
||||
@@ -4056,7 +4012,7 @@ function SPAWN:_OnLand( EventData )
|
||||
-- self:ReSpawn( SpawnGroupIndex )
|
||||
-- Delay respawn by three seconds due to DCS 2.5.4.26368 OB bug https://github.com/FlightControl-Master/MOOSE/issues/1076
|
||||
-- Bug was initially only for engine shutdown event but after ED "fixed" it, it now happens on landing events.
|
||||
SCHEDULER:New( nil, self.ReSpawn, { self, SpawnGroupIndex }, 3 )
|
||||
SCHEDULER:New( nil, self.ReSpawn, { self, SpawnGroupIndex }, self.RepeatOnLandingTime or 3 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
--
|
||||
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a COORDINATE coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Core.Zone}. Optionally, heading and name can be given. The name **must be unique**!
|
||||
--
|
||||
-- @field #SPAWNSTATIC SPAWNSTATIC
|
||||
@@ -411,9 +411,9 @@ function SPAWNSTATIC:Spawn(Heading, NewName)
|
||||
|
||||
end
|
||||
|
||||
--- Creates a new @{Wrapper.Static} from a POINT_VEC2.
|
||||
--- Creates a new @{Wrapper.Static} from a COORDINATE.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
|
||||
-- @param Core.Point#COORDINATE PointVec2 The 2D coordinate where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return Wrapper.Static#STATIC The static spawned.
|
||||
|
||||
@@ -213,7 +213,7 @@ end
|
||||
|
||||
--- Returns if a PointVec3 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test.
|
||||
-- @param Core.Point#COORDINATE PointVec3 The PointVec3 to test.
|
||||
-- @return #boolean true if the PointVec3 is within the zone.
|
||||
function ZONE_BASE:IsPointVec3InZone( PointVec3 )
|
||||
local InZone = self:IsPointVec2InZone( PointVec3 )
|
||||
@@ -227,16 +227,16 @@ function ZONE_BASE:GetVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} of the zone.
|
||||
--- Returns a @{Core.Point#COORDINATE} of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE of the zone.
|
||||
function ZONE_BASE:GetPointVec2()
|
||||
--self:F2( self.ZoneName )
|
||||
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( Vec2 )
|
||||
|
||||
--self:T2( { PointVec2 } )
|
||||
|
||||
@@ -261,16 +261,16 @@ function ZONE_BASE:GetVec3( Height )
|
||||
return Vec3
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC3} of the zone.
|
||||
--- Returns a @{Core.Point#COORDINATE} of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
||||
-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone.
|
||||
-- @return Core.Point#COORDINATE The PointVec3 of the zone.
|
||||
function ZONE_BASE:GetPointVec3( Height )
|
||||
--self:F2( self.ZoneName )
|
||||
|
||||
local Vec3 = self:GetVec3( Height )
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
|
||||
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
|
||||
|
||||
--self:T2( { PointVec3 } )
|
||||
|
||||
@@ -330,16 +330,16 @@ function ZONE_BASE:GetRandomVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_BASE:GetRandomPointVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC3} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_BASE:GetRandomPointVec3()
|
||||
return nil
|
||||
end
|
||||
@@ -605,10 +605,13 @@ function ZONE_BASE:Trigger(Objects)
|
||||
self:AddTransition("TriggerStopped","TriggerStart","TriggerRunning")
|
||||
self:AddTransition("*","EnteredZone","*")
|
||||
self:AddTransition("*","LeftZone","*")
|
||||
self:AddTransition("*","ZoneEmpty","*")
|
||||
self:AddTransition("*","ObjectDead","*")
|
||||
self:AddTransition("*","TriggerRunCheck","*")
|
||||
self:AddTransition("*","TriggerStop","TriggerStopped")
|
||||
self:TriggerStart()
|
||||
self.checkobjects = Objects
|
||||
self.ObjectsInZone = false
|
||||
if UTILS.IsInstanceOf(Objects,"SET_BASE") then
|
||||
self.objectset = Objects.Set
|
||||
else
|
||||
@@ -646,6 +649,22 @@ function ZONE_BASE:Trigger(Objects)
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable leaving the zone.
|
||||
|
||||
--- On After "ObjectDead" event. An observed object has left the zone.
|
||||
-- @function [parent=#ZONE_BASE] OnAfterObjectDead
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable which died. Might be nil.
|
||||
|
||||
--- On After "ZoneEmpty" event. All observed objects have left the zone or are dead.
|
||||
-- @function [parent=#ZONE_BASE] OnAfterZoneEmpty
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
end
|
||||
|
||||
--- (Internal) Check the assigned objects for being in/out of the zone
|
||||
@@ -659,9 +678,13 @@ function ZONE_BASE:_TriggerCheck(fromstart)
|
||||
-- just earmark everyone in/out
|
||||
for _,_object in pairs(objectset) do
|
||||
local obj = _object -- Wrapper.Controllable#CONTROLLABLE
|
||||
if not obj.TriggerInZone then obj.TriggerInZone = {} end
|
||||
if not obj.TriggerInZone then
|
||||
obj.TriggerInZone = {}
|
||||
obj.TriggerZoneDeadNotification = false
|
||||
end
|
||||
if obj and obj:IsAlive() and self:IsCoordinateInZone(obj:GetCoordinate()) then
|
||||
obj.TriggerInZone[self.ZoneName] = true
|
||||
self.ObjectsInZone = true
|
||||
else
|
||||
obj.TriggerInZone[self.ZoneName] = false
|
||||
end
|
||||
@@ -669,6 +692,7 @@ function ZONE_BASE:_TriggerCheck(fromstart)
|
||||
end
|
||||
else
|
||||
-- Check for changes
|
||||
local objcount = 0
|
||||
for _,_object in pairs(objectset) do
|
||||
local obj = _object -- Wrapper.Controllable#CONTROLLABLE
|
||||
if obj and obj:IsAlive() then
|
||||
@@ -683,11 +707,20 @@ function ZONE_BASE:_TriggerCheck(fromstart)
|
||||
-- is obj in zone?
|
||||
local inzone = self:IsCoordinateInZone(obj:GetCoordinate())
|
||||
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
|
||||
if inzone and obj.TriggerInZone[self.ZoneName] then
|
||||
-- just count
|
||||
objcount = objcount + 1
|
||||
self.ObjectsInZone = true
|
||||
obj.TriggerZoneDeadNotification = false
|
||||
end
|
||||
if inzone and not obj.TriggerInZone[self.ZoneName] then
|
||||
-- wasn't in zone before
|
||||
--self:I("Newly entered")
|
||||
self:__EnteredZone(0.5,obj)
|
||||
obj.TriggerInZone[self.ZoneName] = true
|
||||
objcount = objcount + 1
|
||||
self.ObjectsInZone = true
|
||||
obj.TriggerZoneDeadNotification = false
|
||||
elseif (not inzone) and obj.TriggerInZone[self.ZoneName] then
|
||||
-- has left the zone
|
||||
--self:I("Newly left")
|
||||
@@ -696,8 +729,21 @@ function ZONE_BASE:_TriggerCheck(fromstart)
|
||||
else
|
||||
--self:I("Not left or not entered, or something went wrong!")
|
||||
end
|
||||
else
|
||||
-- object dead
|
||||
if not obj.TriggerZoneDeadNotification == true then
|
||||
obj.TriggerInZone = nil
|
||||
self:__ObjectDead(0.5,obj)
|
||||
obj.TriggerZoneDeadNotification = true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- zone empty?
|
||||
if objcount == 0 and self.ObjectsInZone == true then
|
||||
-- zone was not but is now empty
|
||||
self.ObjectsInZone = false
|
||||
self:__ZoneEmpty(0.5)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -768,8 +814,8 @@ end
|
||||
-- Various functions exist to find random points within the zone.
|
||||
--
|
||||
-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#COORDINATE} object representing a random 2D point in the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#COORDINATE} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
||||
--
|
||||
-- ## Draw zone
|
||||
--
|
||||
@@ -964,7 +1010,7 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
|
||||
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y ):Smoke( SmokeColor )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -994,7 +1040,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight )
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y ):Flare( FlareColor, Azimuth )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -1415,7 +1461,7 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = ZoneCoord:GetVec3(),
|
||||
radius = ZoneRadius / 2,
|
||||
radius = ZoneRadius,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1515,15 +1561,15 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
|
||||
return point
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_RADIUS:GetRandomPointVec2( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
|
||||
--self:T3( { PointVec2 } )
|
||||
|
||||
@@ -1546,15 +1592,15 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer )
|
||||
end
|
||||
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC3 The @{Core.Point#POINT_VEC3} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
local PointVec3 = COORDINATE:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
|
||||
--self:T3( { PointVec3 } )
|
||||
|
||||
@@ -1990,15 +2036,15 @@ function ZONE_GROUP:GetRandomVec2()
|
||||
return Point
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_GROUP self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_GROUP:GetRandomPointVec2( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T3( { PointVec2 } )
|
||||
|
||||
@@ -2046,7 +2092,7 @@ function _ZONE_TRIANGLE:New(p1, p2, p3)
|
||||
end
|
||||
|
||||
self.SurfaceArea = math.abs((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) * 0.5
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2054,7 +2100,7 @@ end
|
||||
-- @param #_ZONE_TRIANGLE self
|
||||
-- @param #table pt The point to check
|
||||
-- @param #table points (optional) The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
|
||||
-- @return #bool True if the point is contained, false otherwise
|
||||
-- @return #boolean True if the point is contained, false otherwise
|
||||
function _ZONE_TRIANGLE:ContainsPoint(pt, points)
|
||||
points = points or self.Points
|
||||
|
||||
@@ -2146,8 +2192,8 @@ end
|
||||
-- Various functions exist to find random points within the zone.
|
||||
--
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#COORDINATE} object representing a random 2D point within the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone.
|
||||
--
|
||||
-- ## Draw zone
|
||||
--
|
||||
@@ -2563,7 +2609,7 @@ function ZONE_POLYGON_BASE:ReFill(Color,Alpha)
|
||||
self.FillTriangles = {}
|
||||
end
|
||||
-- refill
|
||||
for _, triangle in pairs(self._Triangles) do
|
||||
for _,triangle in pairs(self._Triangles) do
|
||||
local draw_ids = triangle:Fill(coalition,color,alpha,nil)
|
||||
self.FillTriangles = draw_ids
|
||||
table.combine(self.DrawID, draw_ids)
|
||||
@@ -2723,7 +2769,7 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor, Segments )
|
||||
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
|
||||
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor )
|
||||
COORDINATE:New( PointX, 0, PointY ):Smoke( SmokeColor )
|
||||
end
|
||||
j = i
|
||||
i = i + 1
|
||||
@@ -2758,7 +2804,7 @@ function ZONE_POLYGON_BASE:FlareZone( FlareColor, Segments, Azimuth, AddHeight )
|
||||
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
|
||||
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
POINT_VEC2:New( PointX, PointY, AddHeight ):Flare(FlareColor, Azimuth)
|
||||
COORDINATE:New( PointX, AddHeight, PointY ):Flare(FlareColor, Azimuth)
|
||||
end
|
||||
j = i
|
||||
i = i + 1
|
||||
@@ -2834,26 +2880,26 @@ function ZONE_POLYGON_BASE:GetRandomVec2()
|
||||
end
|
||||
end
|
||||
|
||||
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Return a @{Core.Point#COORDINATE} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return @{Core.Point#POINT_VEC2}
|
||||
-- @return @{Core.Point#COORDINATE}
|
||||
function ZONE_POLYGON_BASE:GetRandomPointVec2()
|
||||
--self:F2()
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T2( PointVec2 )
|
||||
|
||||
return PointVec2
|
||||
end
|
||||
|
||||
--- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return @{Core.Point#POINT_VEC3}
|
||||
-- @return @{Core.Point#COORDINATE}
|
||||
function ZONE_POLYGON_BASE:GetRandomPointVec3()
|
||||
--self:F2()
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec3 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T2( PointVec3 )
|
||||
|
||||
@@ -3536,7 +3582,37 @@ do -- ZONE_ELASTIC
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove a vertex (point) from the polygon.
|
||||
-- @param #ZONE_ELASTIC self
|
||||
-- @param DCS#Vec2 Vec2 Point in 2D (with x and y coordinates).
|
||||
-- @return #ZONE_ELASTIC self
|
||||
function ZONE_ELASTIC:RemoveVertex2D(Vec2)
|
||||
|
||||
local found = false
|
||||
local findex = 0
|
||||
for _id,_vec2 in pairs(self.points) do
|
||||
if _vec2.x == Vec2.x and _vec2.y == Vec2.y then
|
||||
found = true
|
||||
findex = _id
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if found == true and findex > 0 then
|
||||
table.remove(self.points,findex)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove a vertex (point) from the polygon.
|
||||
-- @param #ZONE_ELASTIC self
|
||||
-- @param DCS#Vec3 Vec3 Point in 3D (with x, y and z coordinates). Only the x and z coordinates are used.
|
||||
-- @return #ZONE_ELASTIC self
|
||||
function ZONE_ELASTIC:RemoveVertex3D(Vec3)
|
||||
return self:RemoveVertex2D({x=Vec3.x, y=Vec3.z})
|
||||
end
|
||||
|
||||
--- Add a vertex (point) to the polygon.
|
||||
-- @param #ZONE_ELASTIC self
|
||||
@@ -3574,7 +3650,7 @@ do -- ZONE_ELASTIC
|
||||
|
||||
-- Debug info.
|
||||
--self:T(string.format("Updating ZONE_ELASTIC %s", tostring(self.ZoneName)))
|
||||
|
||||
|
||||
-- Copy all points.
|
||||
local points=UTILS.DeepCopy(self.points or {})
|
||||
|
||||
@@ -3592,6 +3668,9 @@ do -- ZONE_ELASTIC
|
||||
|
||||
-- Update polygon verticies from points.
|
||||
self._.Polygon=self:_ConvexHull(points)
|
||||
|
||||
self._Triangles = self:_Triangulate()
|
||||
self.SurfaceArea = self:_CalculateSurfaceArea()
|
||||
|
||||
if Draw~=false then
|
||||
if self.DrawID or Draw==true then
|
||||
@@ -3790,7 +3869,7 @@ end
|
||||
--- Checks if a point is contained within the oval.
|
||||
-- @param #ZONE_OVAL self
|
||||
-- @param #table point The point to check
|
||||
-- @return #bool True if the point is contained, false otherwise
|
||||
-- @return #boolean True if the point is contained, false otherwise
|
||||
function ZONE_OVAL:IsVec2InZone(vec2)
|
||||
local cos, sin = math.cos, math.sin
|
||||
local dx = vec2.x - self.CenterVec2.x
|
||||
@@ -3852,18 +3931,18 @@ function ZONE_OVAL:GetRandomVec2()
|
||||
return {x=rx, y=ry}
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_OVAL self
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_OVAL:GetRandomPointVec2()
|
||||
return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
|
||||
return COORDINATE:NewFromVec2(self:GetRandomVec2())
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_OVAL self
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_OVAL:GetRandomPointVec3()
|
||||
return POINT_VEC3:NewFromVec3(self:GetRandomVec2())
|
||||
return COORDINATE:NewFromVec3(self:GetRandomVec2())
|
||||
end
|
||||
|
||||
--- Draw the zone on the F10 map.
|
||||
@@ -4003,15 +4082,15 @@ do -- ZONE_AIRBASE
|
||||
return ZoneVec2
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_AIRBASE self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_AIRBASE:GetRandomPointVec2( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T3( { PointVec2 } )
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ function ZONE_DETECTION:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
|
||||
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y):Smoke( SmokeColor )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -138,7 +138,7 @@ function ZONE_DETECTION:FlareZone( FlareColor, Points, Azimuth, AddHeight )
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y ):Flare( FlareColor, Azimuth )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -202,4 +202,3 @@ function ZONE_DETECTION:IsVec3InZone( Vec3 )
|
||||
|
||||
return InZone
|
||||
end
|
||||
|
||||
|
||||
@@ -630,9 +630,13 @@ do -- Object
|
||||
--- @function [parent=#Object] destroy
|
||||
-- @param #Object self
|
||||
|
||||
--- @function [parent=#Object] getCategory
|
||||
--- Returns an enumerator of the category for the specific object.
|
||||
-- The enumerator returned is dependent on the category of the object and how the function is called.
|
||||
-- As of DCS 2.9.2 when this function is called on an Object, Unit, Weapon, or Airbase a 2nd value will be returned which details the object sub-category value.
|
||||
-- @function [parent=#Object] getCategory
|
||||
-- @param #Object self
|
||||
-- @return #Object.Category
|
||||
-- @return #Object.Category The object category (1=UNIT, 2=WEAPON, 3=STATIC, 4=BASE, 5=SCENERY, 6=Cargo)
|
||||
-- @return #number The subcategory of the passed object, e.g. Unit.Category if a unit object was passed.
|
||||
|
||||
--- Returns type name of the Object.
|
||||
-- @function [parent=#Object] getTypeName
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
-- ### Author: FlightControl - Framework Design & Programming
|
||||
-- ### Refactoring to use the Runway auto-detection: Applevangelist
|
||||
-- @date August 2022
|
||||
-- Last Update Oct 2024
|
||||
-- Last Update Feb 2025
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -416,7 +416,7 @@ end
|
||||
-- @field #ATC_GROUND_UNIVERSAL
|
||||
ATC_GROUND_UNIVERSAL = {
|
||||
ClassName = "ATC_GROUND_UNIVERSAL",
|
||||
Version = "0.0.1",
|
||||
Version = "0.0.2",
|
||||
SetClient = nil,
|
||||
Airbases = nil,
|
||||
AirbaseList = nil,
|
||||
@@ -441,17 +441,25 @@ function ATC_GROUND_UNIVERSAL:New(AirbaseList)
|
||||
self:T( { self.ClassName } )
|
||||
|
||||
self.Airbases = {}
|
||||
|
||||
for _name,_ in pairs(_DATABASE.AIRBASES) do
|
||||
self.Airbases[_name]={}
|
||||
end
|
||||
|
||||
self.AirbaseList = AirbaseList
|
||||
|
||||
if not self.AirbaseList then
|
||||
self.AirbaseList = {}
|
||||
for _name,_ in pairs(_DATABASE.AIRBASES) do
|
||||
self.AirbaseList[_name]=_name
|
||||
for _name,_base in pairs(_DATABASE.AIRBASES) do
|
||||
-- DONE exclude FARPS and Ships
|
||||
if _base and _base.isAirdrome == true then
|
||||
self.AirbaseList[_name]=_name
|
||||
self.Airbases[_name]={}
|
||||
end
|
||||
end
|
||||
else
|
||||
for _,_name in pairs(AirbaseList) do
|
||||
-- DONE exclude FARPS and Ships
|
||||
local airbase = _DATABASE:FindAirbase(_name)
|
||||
if airbase and (airbase.isAirdrome == true) then
|
||||
self.Airbases[_name]={}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1447,11 +1455,10 @@ function ATC_GROUND_PERSIANGULF:Start( RepeatScanSeconds )
|
||||
self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds )
|
||||
end
|
||||
|
||||
|
||||
-- @type ATC_GROUND_MARIANAISLANDS
|
||||
---
|
||||
-- @type ATC_GROUND_MARIANAISLANDS
|
||||
-- @extends #ATC_GROUND
|
||||
|
||||
|
||||
|
||||
--- # ATC\_GROUND\_MARIANA, extends @{#ATC_GROUND}
|
||||
--
|
||||
|
||||
@@ -619,63 +619,148 @@ ARTY.WeaponType={
|
||||
}
|
||||
|
||||
--- Database of common artillery unit properties.
|
||||
-- @type ARTY.dbitem
|
||||
-- @field #string displayname Name displayed in ME.
|
||||
-- @field #number minrange Minimum firing range in meters.
|
||||
-- @field #number maxrange Maximum firing range in meters.
|
||||
-- @field #number reloadtime Reload time in seconds.
|
||||
|
||||
--- Database of common artillery unit properties.
|
||||
-- Table key is the "type name" and table value is and `ARTY.dbitem`.
|
||||
-- @type ARTY.db
|
||||
ARTY.db={
|
||||
["2B11 mortar"] = { -- type "2B11 mortar"
|
||||
minrange = 500, -- correct?
|
||||
maxrange = 7000, -- 7 km
|
||||
reloadtime = 30, -- 30 sec
|
||||
["LeFH_18-40-105"] = {
|
||||
displayname = "FH LeFH-18 105mm", -- name displayed in the ME
|
||||
minrange = 500, -- min range (green circle) in meters
|
||||
maxrange = 10500, -- max range (red circle) in meters
|
||||
reloadtime = nil, -- reload time in seconds
|
||||
},
|
||||
["SPH 2S1 Gvozdika"] = { -- type "SAU Gvozdika"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 15000, -- 15 km
|
||||
reloadtime = nil, -- unknown
|
||||
["M2A1-105"] = {
|
||||
displayname = "FH M2A1 105mm",
|
||||
minrange = 500,
|
||||
maxrange = 11500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SPH 2S19 Msta"] = { --type "SAU Msta", alias "2S19 Msta"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 23500, -- 23.5 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Pak40"] = {
|
||||
displayname = "FH Pak 40 75mm",
|
||||
minrange = 500,
|
||||
maxrange = 3000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["L118_Unit"] = {
|
||||
displayname = "L118 Light Artillery Gun",
|
||||
minrange = 500,
|
||||
maxrange = 17500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SPH 2S3 Akatsia"] = { -- type "SAU Akatsia", alias "2S3 Akatsia"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 17000, -- 17 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Smerch"] = {
|
||||
displayname = "MLRS 9A52 Smerch CM 300mm",
|
||||
minrange = 20000,
|
||||
maxrange = 70000,
|
||||
reloadtime = 2160,
|
||||
},
|
||||
["SPH 2S9 Nona"] = { --type "SAU 2-C9"
|
||||
minrange = 500, -- correct?
|
||||
maxrange = 7000, -- 7 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Smerch_HE"] = {
|
||||
displayname = "MLRS 9A52 Smerch HE 300mm",
|
||||
minrange = 20000,
|
||||
maxrange = 70000,
|
||||
reloadtime = 2160,
|
||||
},
|
||||
["SPH M109 Paladin"] = { -- type "M-109", alias "M109"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 22000, -- 22 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Uragan_BM-27"] = {
|
||||
displayname = "MLRS 9K57 Uragan BM-27 220mm",
|
||||
minrange = 11500,
|
||||
maxrange = 35800,
|
||||
reloadtime = 840,
|
||||
},
|
||||
["SpGH Dana"] = { -- type "SpGH_Dana"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 18700, -- 18.7 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Grad-URAL"] = {
|
||||
displayname = "MLRS BM-21 Grad 122mm",
|
||||
minrange = 5000,
|
||||
maxrange = 19000,
|
||||
reloadtime = 420,
|
||||
},
|
||||
["MLRS BM-21 Grad"] = { --type "Grad-URAL", alias "MLRS BM-21 Grad"
|
||||
minrange = 5000, -- 5 km
|
||||
maxrange = 19000, -- 19 km
|
||||
reloadtime = 420, -- 7 min
|
||||
["HL_B8M1"] = {
|
||||
displayname = "MLRS HL with B8M1 80mm",
|
||||
minrange = 500,
|
||||
maxrange = 5000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["MLRS 9K57 Uragan BM-27"] = { -- type "Uragan_BM-27"
|
||||
minrange = 11500, -- 11.5 km
|
||||
maxrange = 35800, -- 35.8 km
|
||||
reloadtime = 840, -- 14 min
|
||||
["tt_B8M1"] = {
|
||||
displayname = "MLRS LC with B8M1 80mm",
|
||||
minrange = 500,
|
||||
maxrange = 5000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["MLRS 9A52 Smerch"] = { -- type "Smerch"
|
||||
minrange = 20000, -- 20 km
|
||||
maxrange = 70000, -- 70 km
|
||||
reloadtime = 2160, -- 36 min
|
||||
["MLRS"] = {
|
||||
displayname = "MLRS M270 227mm",
|
||||
minrange = 10000,
|
||||
maxrange = 32000,
|
||||
reloadtime = 540,
|
||||
},
|
||||
["MLRS M270"] = { --type "MRLS", alias "M270 MRLS"
|
||||
minrange = 10000, -- 10 km
|
||||
maxrange = 32000, -- 32 km
|
||||
reloadtime = 540, -- 9 min
|
||||
["2B11 mortar"] = {
|
||||
displayname = "Mortar 2B11 120mm",
|
||||
minrange = 500,
|
||||
maxrange = 7000,
|
||||
reloadtime = 30,
|
||||
},
|
||||
["PLZ05"] = {
|
||||
displayname = "PLZ-05",
|
||||
minrange = 500,
|
||||
maxrange = 23500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU Gvozdika"] = {
|
||||
displayname = "SPH 2S1 Gvozdika 122mm",
|
||||
minrange = 300,
|
||||
maxrange = 15000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU Msta"] = {
|
||||
displayname = "SPH 2S19 Msta 152mm",
|
||||
minrange = 300,
|
||||
maxrange = 23500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU Akatsia"] = {
|
||||
displayname = "SPH 2S3 Akatsia 152mm",
|
||||
minrange = 300,
|
||||
maxrange = 17000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SpGH_Dana"] = {
|
||||
displayname = "SPH Dana vz77 152mm",
|
||||
minrange = 300,
|
||||
maxrange = 18700,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["M-109"] = {
|
||||
displayname = "SPH M109 Paladin 155mm",
|
||||
minrange = 300,
|
||||
maxrange = 22000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["M12_GMC"] = {
|
||||
displayname = "SPH M12 GMC 155mm",
|
||||
minrange = 300,
|
||||
maxrange = 18200,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["Wespe124"] = {
|
||||
displayname = "SPH Sd.Kfz.124 Wespe 105mm",
|
||||
minrange = 300,
|
||||
maxrange = 7000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["T155_Firtina"] = {
|
||||
displayname = "SPH T155 Firtina 155mm",
|
||||
minrange = 300,
|
||||
maxrange = 41000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU 2-C9"] = {
|
||||
displayname = "SPM 2S9 Nona 120mm M",
|
||||
minrange = 500,
|
||||
maxrange = 7000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
}
|
||||
|
||||
--- Target.
|
||||
@@ -695,7 +780,7 @@ ARTY.db={
|
||||
|
||||
--- Arty script version.
|
||||
-- @field #string version
|
||||
ARTY.version="1.3.1"
|
||||
ARTY.version="1.3.3"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -797,8 +882,8 @@ function ARTY:New(group, alias)
|
||||
-- Maximum speed in km/h.
|
||||
self.SpeedMax=group:GetSpeedMax()
|
||||
|
||||
-- Group is mobile or not (e.g. mortars).
|
||||
if self.SpeedMax>1 then
|
||||
-- Group is mobile or not (e.g. mortars). Some immobile units have a speed of 1 m/s = 3.6 km/h. So we check this number.
|
||||
if self.SpeedMax>3.6 then
|
||||
self.ismobile=true
|
||||
else
|
||||
self.ismobile=false
|
||||
@@ -1923,7 +2008,7 @@ function ARTY:onafterStart(Controllable, From, Event, To)
|
||||
end
|
||||
|
||||
-- Check if we have and arty type that is in the DB.
|
||||
local _dbproperties=self:_CheckDB(self.DisplayName)
|
||||
local _dbproperties=self:_CheckDB(self.Type)
|
||||
self:T({dbproperties=_dbproperties})
|
||||
if _dbproperties~=nil then
|
||||
for property,value in pairs(_dbproperties) do
|
||||
@@ -1969,8 +2054,8 @@ function ARTY:onafterStart(Controllable, From, Event, To)
|
||||
text=text..string.format("Type = %s\n", self.Type)
|
||||
text=text..string.format("Display Name = %s\n", self.DisplayName)
|
||||
text=text..string.format("Number of units = %d\n", self.IniGroupStrength)
|
||||
text=text..string.format("Speed max = %d km/h\n", self.SpeedMax)
|
||||
text=text..string.format("Speed default = %d km/h\n", self.Speed)
|
||||
text=text..string.format("Speed max = %.1f km/h\n", self.SpeedMax)
|
||||
text=text..string.format("Speed default = %.1f km/h\n", self.Speed)
|
||||
text=text..string.format("Is mobile = %s\n", tostring(self.ismobile))
|
||||
text=text..string.format("Is cargo = %s\n", tostring(self.iscargo))
|
||||
text=text..string.format("Min range = %.1f km\n", self.minrange/1000)
|
||||
@@ -3049,7 +3134,7 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target)
|
||||
local nfire=Narty
|
||||
local _type="shots"
|
||||
if target.weapontype==ARTY.WeaponType.Auto then
|
||||
nfire=Narty
|
||||
nfire=Nammo -- We take everything that is available
|
||||
_type="shots"
|
||||
elseif target.weapontype==ARTY.WeaponType.Cannon then
|
||||
nfire=Narty
|
||||
@@ -3070,6 +3155,8 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target)
|
||||
nfire=Nmissiles
|
||||
_type="cruise missiles"
|
||||
end
|
||||
|
||||
--env.info(string.format("FF type=%s, Nrockets=%d, Nfire=%d target.nshells=%d", _type, Nrockets, nfire, target.nshells))
|
||||
|
||||
-- Adjust if less than requested ammo is left.
|
||||
target.nshells=math.min(target.nshells, nfire)
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
-- @image Designation.JPG
|
||||
--
|
||||
-- Date: 24 Oct 2021
|
||||
-- Last Update: May 2024
|
||||
-- Last Update: Mar 2025
|
||||
--
|
||||
--- Class AUTOLASE
|
||||
-- @type AUTOLASE
|
||||
@@ -89,6 +89,10 @@
|
||||
-- @field #table playermenus
|
||||
-- @field #boolean smokemenu
|
||||
-- @field #boolean threatmenu
|
||||
-- @field #number RoundingPrecision
|
||||
-- @field #table smokeoffset
|
||||
-- @field #boolean increasegroundawareness
|
||||
-- @field #number MonitorFrequency
|
||||
-- @extends Ops.Intel#INTEL
|
||||
|
||||
---
|
||||
@@ -100,6 +104,9 @@ AUTOLASE = {
|
||||
alias = "",
|
||||
debug = false,
|
||||
smokemenu = true,
|
||||
RoundingPrecision = 0,
|
||||
increasegroundawareness = true,
|
||||
MonitorFrequency = 30,
|
||||
}
|
||||
|
||||
--- Laser spot info
|
||||
@@ -118,7 +125,7 @@ AUTOLASE = {
|
||||
|
||||
--- AUTOLASE class version.
|
||||
-- @field #string version
|
||||
AUTOLASE.version = "0.1.26"
|
||||
AUTOLASE.version = "0.1.31"
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Begin Functional.Autolase.lua
|
||||
@@ -191,6 +198,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
||||
self.reporttimelong = 30
|
||||
self.smoketargets = false
|
||||
self.smokecolor = SMOKECOLOR.Red
|
||||
self.smokeoffset = nil
|
||||
self.notifypilots = true
|
||||
self.targetsperrecce = {}
|
||||
self.RecceUnits = {}
|
||||
@@ -207,6 +215,11 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
||||
self.playermenus = {}
|
||||
self.smokemenu = true
|
||||
self.threatmenu = true
|
||||
self.RoundingPrecision = 0
|
||||
self.increasegroundawareness = true
|
||||
self.MonitorFrequency = 30
|
||||
|
||||
self:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)})
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
||||
@@ -309,16 +322,41 @@ end
|
||||
-- Helper Functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
--- [User] When using Monitor, set the frequency here in which the report will appear
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #number Seconds Run the report loop every number of seconds defined here.
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetMonitorFrequency(Seconds)
|
||||
self.MonitorFrequency = Seconds or 30
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set a table of possible laser codes.
|
||||
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 } .
|
||||
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 }.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #list<#number> LaserCodes
|
||||
-- @return #AUTOLASE
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetLaserCodes( LaserCodes )
|
||||
self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes }
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Improve ground unit detection by using a zone scan and LOS check.
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:EnableImproveGroundUnitsDetection()
|
||||
self.increasegroundawareness = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Do not improve ground unit detection by using a zone scan and LOS check.
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:DisableImproveGroundUnitsDetection()
|
||||
self.increasegroundawareness = false
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Function to set pilot menu.
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
@@ -600,11 +638,26 @@ function AUTOLASE:SetSmokeTargets(OnOff,Color)
|
||||
return self
|
||||
end
|
||||
|
||||
--- (User) Function to set rounding precision for BR distance output.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #number IDP Rounding precision before/after the decimal sign. Defaults to zero. Positive values round right of the decimal sign, negative ones left of the decimal sign.
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetRoundingPrecsion(IDP)
|
||||
self.RoundingPrecision = IDP or 0
|
||||
return self
|
||||
end
|
||||
|
||||
--- (User) Show the "Switch smoke target..." menu entry for pilots. On by default.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #table Offset (Optional) Define an offset for the smoke, i.e. not directly on the unit itself, angle is degrees and distance is meters. E.g. `autolase:EnableSmokeMenu({Angle=30,Distance=20})`
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:EnableSmokeMenu()
|
||||
function AUTOLASE:EnableSmokeMenu(Offset)
|
||||
self.smokemenu = true
|
||||
if Offset then
|
||||
self.smokeoffset = {}
|
||||
self.smokeoffset.Distance = Offset.Distance or math.random(10,20)
|
||||
self.smokeoffset.Angle = Offset.Angle or math.random(0,359)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -613,6 +666,7 @@ end
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:DisableSmokeMenu()
|
||||
self.smokemenu = false
|
||||
self.smokeoffset = nil
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -671,7 +725,8 @@ function AUTOLASE:CleanCurrentLasing()
|
||||
local unit = recce:GetUnit(1)
|
||||
local name = unit:GetName()
|
||||
if not self.RecceUnits[name] then
|
||||
self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime() }
|
||||
local isground = (unit and unit.IsGround) and unit:IsGround() or false
|
||||
self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime(), isground=isground }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -786,8 +841,12 @@ function AUTOLASE:ShowStatus(Group,Unit)
|
||||
locationstring = entry.coordinate:ToStringMGRS(settings)
|
||||
elseif settings:IsA2G_LL_DMS() then
|
||||
locationstring = entry.coordinate:ToStringLLDMS(settings)
|
||||
elseif settings:IsA2G_LL_DDM() then
|
||||
locationstring = entry.coordinate:ToStringLLDDM(settings)
|
||||
elseif settings:IsA2G_BR() then
|
||||
locationstring = entry.coordinate:ToStringBR(Group:GetCoordinate() or Unit:GetCoordinate(),settings)
|
||||
-- attention this is the distance from the ASKING unit to target, not from RECCE to target!
|
||||
local startcoordinate = Unit:GetCoordinate() or Group:GetCoordinate()
|
||||
locationstring = entry.coordinate:ToStringBR(startcoordinate,settings,false,self.RoundingPrecision)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -917,6 +976,65 @@ function AUTOLASE:CanLase(Recce,Unit)
|
||||
return canlase
|
||||
end
|
||||
|
||||
--- (Internal) Function to do a zone check per ground Recce and make found units and statics "known".
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:_Prescient()
|
||||
-- self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime(), isground=isground }
|
||||
for _,_data in pairs(self.RecceUnits) do
|
||||
-- ground units only
|
||||
if _data.isground and _data.unit and _data.unit:IsAlive() then
|
||||
local unit = _data.unit -- Wrapper.Unit#UNIT
|
||||
local position = unit:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local needsinit = false
|
||||
if position then
|
||||
local lastposition = unit:GetProperty("lastposition")
|
||||
-- property initiated?
|
||||
if not lastposition then
|
||||
unit:SetProperty("lastposition",position)
|
||||
lastposition = position
|
||||
needsinit = true
|
||||
end
|
||||
-- has moved?
|
||||
local dist = position:Get2DDistance(lastposition)
|
||||
-- refresh?
|
||||
local TNow = timer.getAbsTime()
|
||||
-- check
|
||||
if dist > 10 or needsinit==true or TNow - _data.timestamp > 29 then
|
||||
-- init scan objects
|
||||
local hasunits,hasstatics,_,Units,Statics = position:ScanObjects(self.LaseDistance,true,true,false)
|
||||
-- loop found units
|
||||
if hasunits then
|
||||
self:T(self.lid.."Checking possibly visible UNITs for Recce "..unit:GetName())
|
||||
for _,_target in pairs(Units) do -- Wrapper.Unit#UNIT object here
|
||||
local target = _target -- Wrapper.Unit#UNIT
|
||||
if target and target:GetCoalition() ~= self.coalition then
|
||||
if unit:IsLOS(target) and (not target:IsUnitDetected(unit))then
|
||||
unit:KnowUnit(target,true,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- loop found statics
|
||||
if hasstatics then
|
||||
self:T(self.lid.."Checking possibly visible STATICs for Recce "..unit:GetName())
|
||||
for _,_static in pairs(Statics) do -- DCS static object here
|
||||
local static = STATIC:Find(_static)
|
||||
if static and static:GetCoalition() ~= self.coalition then
|
||||
local IsLOS = position:IsLOS(static:GetCoordinate())
|
||||
if IsLOS then
|
||||
unit:KnowUnit(static,true,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- FSM Functions
|
||||
-------------------------------------------------------------------
|
||||
@@ -929,6 +1047,9 @@ end
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:onbeforeMonitor(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
if self.increasegroundawareness then
|
||||
self:_Prescient()
|
||||
end
|
||||
-- Check if group has detected any units.
|
||||
self:UpdateIntel()
|
||||
return self
|
||||
@@ -957,7 +1078,7 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
local grp = contact.group
|
||||
local coord = contact.position
|
||||
local reccename = contact.recce or "none"
|
||||
local threat = contact.threatlevel or 0
|
||||
local threat = contact.threatlevel or 0
|
||||
local reccegrp = UNIT:FindByName(reccename)
|
||||
if reccegrp then
|
||||
local reccecoord = reccegrp:GetCoordinate()
|
||||
@@ -1081,6 +1202,9 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
}
|
||||
if self.smoketargets then
|
||||
local coord = unit:GetCoordinate()
|
||||
if self.smokeoffset then
|
||||
coord:Translate(self.smokeoffset.Distance,self.smokeoffset.Angle,true,true)
|
||||
end
|
||||
local color = self:GetSmokeColor(reccename)
|
||||
coord:Smoke(color)
|
||||
end
|
||||
@@ -1091,7 +1215,8 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
end
|
||||
end
|
||||
|
||||
self:__Monitor(-30)
|
||||
local nextloop = -self.MonitorFrequency or -30
|
||||
self:__Monitor(nextloop)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -357,7 +357,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
|
||||
self:F({Event})
|
||||
|
||||
|
||||
if Event.IniDCSUnit and Event.IniCategory == Object.Category.UNIT then
|
||||
if Event.IniDCSUnit and Event.IniUnit and Event.IniCategory == Object.Category.UNIT then
|
||||
if self.CleanUpList[Event.IniDCSUnitName] == nil then
|
||||
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
|
||||
self:AddForCleanUp( Event.IniUnit, Event.IniDCSUnitName )
|
||||
@@ -365,7 +365,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
|
||||
end
|
||||
end
|
||||
|
||||
if Event.TgtDCSUnit and Event.TgtCategory == Object.Category.UNIT then
|
||||
if Event.TgtDCSUnit and Event.TgtUnit and Event.TgtCategory == Object.Category.UNIT then
|
||||
if self.CleanUpList[Event.TgtDCSUnitName] == nil then
|
||||
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
|
||||
self:AddForCleanUp( Event.TgtUnit, Event.TgtDCSUnitName )
|
||||
@@ -387,7 +387,7 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
|
||||
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
|
||||
|
||||
if CleanUpUnit:IsAlive() ~= nil then
|
||||
if CleanUpUnit and CleanUpUnit:IsAlive() ~= nil then
|
||||
|
||||
if self:IsInAirbase( CleanUpUnit:GetVec2() ) then
|
||||
|
||||
@@ -414,7 +414,7 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
end
|
||||
end
|
||||
-- Clean Units which are waiting for a very long time in the CleanUpZone.
|
||||
if CleanUpUnit and not CleanUpUnit:GetPlayerName() then
|
||||
if CleanUpUnit and (CleanUpUnit.GetPlayerName == nil or not CleanUpUnit:GetPlayerName()) then
|
||||
local CleanUpUnitVelocity = CleanUpUnit:GetVelocityKMH()
|
||||
if CleanUpUnitVelocity < 1 then
|
||||
if CleanUpListData.CleanUpMoved then
|
||||
|
||||
@@ -1154,8 +1154,6 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
|
||||
if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then
|
||||
|
||||
if true then
|
||||
|
||||
local EscortGroupName = self.EscortGroup:GetName()
|
||||
|
||||
self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
|
||||
@@ -1226,177 +1224,6 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
end
|
||||
|
||||
return true
|
||||
else
|
||||
-- local EscortGroupName = self.EscortGroup:GetName()
|
||||
-- local EscortTargets = self.EscortGroup:GetDetectedTargets()
|
||||
--
|
||||
-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets
|
||||
--
|
||||
-- local EscortTargetMessages = ""
|
||||
-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do
|
||||
-- local EscortObject = EscortTarget.object
|
||||
-- self:T( EscortObject )
|
||||
-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then
|
||||
--
|
||||
-- local EscortTargetUnit = UNIT:Find( EscortObject )
|
||||
-- local EscortTargetUnitName = EscortTargetUnit:GetName()
|
||||
--
|
||||
--
|
||||
--
|
||||
-- -- local EscortTargetIsDetected,
|
||||
-- -- EscortTargetIsVisible,
|
||||
-- -- EscortTargetLastTime,
|
||||
-- -- EscortTargetKnowType,
|
||||
-- -- EscortTargetKnowDistance,
|
||||
-- -- EscortTargetLastPos,
|
||||
-- -- EscortTargetLastVelocity
|
||||
-- -- = self.EscortGroup:IsTargetDetected( EscortObject )
|
||||
-- --
|
||||
-- -- self:T( { EscortTargetIsDetected,
|
||||
-- -- EscortTargetIsVisible,
|
||||
-- -- EscortTargetLastTime,
|
||||
-- -- EscortTargetKnowType,
|
||||
-- -- EscortTargetKnowDistance,
|
||||
-- -- EscortTargetLastPos,
|
||||
-- -- EscortTargetLastVelocity } )
|
||||
--
|
||||
--
|
||||
-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3()
|
||||
-- local EscortVec3 = self.EscortGroup:GetVec3()
|
||||
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
|
||||
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
|
||||
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
|
||||
-- ) ^ 0.5 / 1000
|
||||
--
|
||||
-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } )
|
||||
--
|
||||
-- if Distance <= 15 then
|
||||
--
|
||||
-- if not ClientEscortTargets[EscortTargetUnitName] then
|
||||
-- ClientEscortTargets[EscortTargetUnitName] = {}
|
||||
-- end
|
||||
-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit
|
||||
-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible
|
||||
-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type
|
||||
-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance
|
||||
-- else
|
||||
-- if ClientEscortTargets[EscortTargetUnitName] then
|
||||
-- ClientEscortTargets[EscortTargetUnitName] = nil
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- self:T( { "Sorting Targets Table:", ClientEscortTargets } )
|
||||
-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end )
|
||||
-- self:T( { "Sorted Targets Table:", ClientEscortTargets } )
|
||||
--
|
||||
-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
|
||||
-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
|
||||
--
|
||||
-- if self.EscortMenuTargetAssistance then
|
||||
-- self.EscortMenuTargetAssistance:RemoveSubMenus()
|
||||
-- end
|
||||
--
|
||||
-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do
|
||||
-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } )
|
||||
-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove()
|
||||
-- --end
|
||||
--
|
||||
--
|
||||
-- if ClientEscortTargets then
|
||||
-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do
|
||||
--
|
||||
-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
|
||||
--
|
||||
-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then
|
||||
--
|
||||
-- local EscortTargetMessage = ""
|
||||
-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName()
|
||||
-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName()
|
||||
-- if ClientEscortTargetData.type then
|
||||
-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at "
|
||||
-- else
|
||||
-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at "
|
||||
-- end
|
||||
--
|
||||
-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3()
|
||||
-- local EscortVec3 = self.EscortGroup:GetVec3()
|
||||
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
|
||||
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
|
||||
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
|
||||
-- ) ^ 0.5 / 1000
|
||||
--
|
||||
-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } )
|
||||
-- if ClientEscortTargetData.visible == false then
|
||||
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km"
|
||||
-- else
|
||||
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km"
|
||||
-- end
|
||||
--
|
||||
-- if ClientEscortTargetData.visible then
|
||||
-- EscortTargetMessage = EscortTargetMessage .. ", visual"
|
||||
-- end
|
||||
--
|
||||
-- if ClientEscortGroupName == EscortGroupName then
|
||||
--
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient,
|
||||
-- EscortTargetMessage,
|
||||
-- self.EscortMenuAttackNearbyTargets,
|
||||
-- ESCORT._AttackTarget,
|
||||
-- { ParamSelf = self,
|
||||
-- ParamUnit = ClientEscortTargetData.AttackUnit
|
||||
-- }
|
||||
-- )
|
||||
-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage
|
||||
-- else
|
||||
-- if self.EscortMenuTargetAssistance then
|
||||
-- local MenuTargetAssistance = MENU_GROUP:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient,
|
||||
-- EscortTargetMessage,
|
||||
-- MenuTargetAssistance,
|
||||
-- ESCORT._AssistTarget,
|
||||
-- self,
|
||||
-- EscortGroupData.EscortGroup,
|
||||
-- ClientEscortTargetData.AttackUnit
|
||||
-- )
|
||||
-- end
|
||||
-- end
|
||||
-- else
|
||||
-- ClientEscortTargetData = nil
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- if EscortTargetMessages ~= "" and self.ReportTargets == true then
|
||||
-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient )
|
||||
-- else
|
||||
-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient )
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- if self.EscortMenuResumeMission then
|
||||
-- self.EscortMenuResumeMission:RemoveSubMenus()
|
||||
--
|
||||
-- -- if self.EscortMenuResumeWayPoints then
|
||||
-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do
|
||||
-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } )
|
||||
-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove()
|
||||
-- -- end
|
||||
-- -- end
|
||||
--
|
||||
-- local TaskPoints = self:RegisterRoute()
|
||||
-- for WayPointID, WayPoint in pairs( TaskPoints ) do
|
||||
-- local EscortVec3 = self.EscortGroup:GetVec3()
|
||||
-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 +
|
||||
-- ( WayPoint.y - EscortVec3.z )^2
|
||||
-- ) ^ 0.5 / 1000
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
@@ -1060,7 +1060,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
|
||||
-- Tracking info and init of last bomb position.
|
||||
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
|
||||
self:I(FOX.lid..text)
|
||||
self:T(FOX.lid..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
|
||||
-- Loop over players.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
-- @module Functional.Mantis
|
||||
-- @image Functional.Mantis.jpg
|
||||
--
|
||||
-- Last Update: Jan 2025
|
||||
-- Last Update: Mar 2025
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends Core.Base#BASE
|
||||
@@ -60,6 +60,9 @@
|
||||
-- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius.
|
||||
-- @field #boolean checkforfriendlies If true, do not activate a SAM installation if a friendly aircraft is in firing range.
|
||||
-- @field #table FilterZones Table of Core.Zone#ZONE Zones Consider SAM groups in this zone(s) only for this MANTIS instance, must be handed as #table of Zone objects.
|
||||
-- @field #boolean SmokeDecoy If true, smoke short range SAM units as decoy if a plane is in firing range.
|
||||
-- @field #number SmokeDecoyColor Color to use, defaults to SMOKECOLOR.White
|
||||
-- @field #number checkcounter Counter for SAM Table refreshes
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -71,7 +74,7 @@
|
||||
--
|
||||
-- * Moose derived Modular, Automatic and Network capable Targeting and Interception System.
|
||||
-- * Controls a network of SAM sites. Uses detection to switch on the SAM site closest to the enemy.
|
||||
-- * **Automatic mode** (default since 0.8) can set-up your SAM site network automatically for you
|
||||
-- * **Automatic mode** (default since 0.8) will set-up your SAM site network automatically for you
|
||||
-- * **Classic mode** behaves like before
|
||||
-- * Leverage evasiveness from SEAD, leverage attack range setting
|
||||
-- * Automatic setup of SHORAD based on groups of the class "short-range"
|
||||
@@ -86,6 +89,7 @@
|
||||
-- * SAM sites, e.g. each **group name** begins with "Red SAM"
|
||||
-- * EWR network and AWACS, e.g. each **group name** begins with "Red EWR" and *not* e.g. "Red SAM EWR" (overlap with "Red SAM"), "Red EWR Awacs" will be found by "Red EWR"
|
||||
-- * SHORAD, e.g. each **group name** begins with "Red SHORAD" and *not" e.g. just "SHORAD" because you might also have "Blue SHORAD"
|
||||
-- * Point Defense, e.g. each **group name** begins with "Red AAA" and *not" e.g. just "AAA" because you might also have "Blue AAA"
|
||||
--
|
||||
-- It's important to get this right because of the nature of the filter-system in @{Core.Set#SET_GROUP}. Filters are "greedy", that is they
|
||||
-- will match *any* string that contains the search string - hence we need to avoid that SAMs, EWR and SHORAD step on each other\'s toes.
|
||||
@@ -144,6 +148,7 @@
|
||||
-- **Location** is of highest importance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system
|
||||
-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units
|
||||
-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do.
|
||||
-- **HINT** Set at least one EWR on invisible and immortal so MANTIS doesn't stop working.
|
||||
--
|
||||
-- ## 1.2 SAM sites
|
||||
--
|
||||
@@ -192,26 +197,24 @@
|
||||
-- mybluemantis:AddZones(AcceptZones,RejectZones,ConflictZones)
|
||||
--
|
||||
--
|
||||
-- ### 2.1.2 Change the number of long-, mid- and short-range systems going live on a detected target:
|
||||
-- ### 2.1.2 Change the number of long-, mid- and short-range, point defense systems going live on a detected target:
|
||||
--
|
||||
-- -- parameters are numbers. Defaults are 1,2,2,6 respectively
|
||||
-- mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic)
|
||||
-- -- parameters are numbers. Defaults are 1,2,2,6,6 respectively
|
||||
-- mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic,Point)
|
||||
--
|
||||
-- ### 2.1.3 SHORAD will automatically be added from SAM sites of type "short-range"
|
||||
-- ### 2.1.3 SHORAD/Point defense will automatically be added from SAM sites of type "point" or if the range is less than 5km or if the type is AAA.
|
||||
--
|
||||
-- ### 2.1.4 Advanced features
|
||||
--
|
||||
-- -- switch off auto mode **before** you start MANTIS.
|
||||
-- -- Option to switch off auto mode **before** you start MANTIS (not recommended)
|
||||
-- mybluemantis.automode = false
|
||||
--
|
||||
-- -- switch off auto shorad **before** you start MANTIS.
|
||||
-- mybluemantis.autoshorad = false
|
||||
--
|
||||
-- -- scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
|
||||
-- -- Option to set the scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
|
||||
-- -- also see engagerange below.
|
||||
-- self.radiusscale[MANTIS.SamType.LONG] = 1.1
|
||||
-- self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
|
||||
-- self.radiusscale[MANTIS.SamType.SHORT] = 1.3
|
||||
-- self.radiusscale[MANTIS.SamType.POINT] = 1.4
|
||||
--
|
||||
-- ### 2.1.5 Friendlies check in firing range
|
||||
--
|
||||
@@ -240,9 +243,9 @@
|
||||
--
|
||||
-- Use this option if you want to make use of or allow advanced SEAD tactics.
|
||||
--
|
||||
-- # 5. Integrate SHORAD [classic mode, not necessary in automode]
|
||||
-- # 5. Integrate SHORAD [classic mode, not necessary in automode, not recommended for manual setup]
|
||||
--
|
||||
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
|
||||
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs manually. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
|
||||
-- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so:
|
||||
--
|
||||
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
|
||||
@@ -296,6 +299,7 @@ MANTIS = {
|
||||
SAM_Table_Long = {},
|
||||
SAM_Table_Medium = {},
|
||||
SAM_Table_Short = {},
|
||||
SAM_Table_PointDef = {},
|
||||
lid = "",
|
||||
Detection = nil,
|
||||
AWACS_Detection = nil,
|
||||
@@ -329,6 +333,9 @@ MANTIS = {
|
||||
autoshorad = true,
|
||||
ShoradGroupSet = nil,
|
||||
checkforfriendlies = false,
|
||||
SmokeDecoy = false,
|
||||
SmokeDecoyColor = SMOKECOLOR.White,
|
||||
checkcounter = 1,
|
||||
}
|
||||
|
||||
--- Advanced state enumerator
|
||||
@@ -345,8 +352,17 @@ MANTIS.SamType = {
|
||||
SHORT = "Short",
|
||||
MEDIUM = "Medium",
|
||||
LONG = "Long",
|
||||
POINT = "Point",
|
||||
}
|
||||
|
||||
--- SAM Radiusscale
|
||||
-- @type MANTIS.radiusscale
|
||||
MANTIS.radiusscale = {}
|
||||
MANTIS.radiusscale[MANTIS.SamType.LONG] = 1.1
|
||||
MANTIS.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
|
||||
MANTIS.radiusscale[MANTIS.SamType.SHORT] = 1.75
|
||||
MANTIS.radiusscale[MANTIS.SamType.POINT] = 3
|
||||
|
||||
--- SAM data
|
||||
-- @type MANTIS.SamData
|
||||
-- @field #number Range Max firing range in km
|
||||
@@ -354,6 +370,7 @@ MANTIS.SamType = {
|
||||
-- @field #number Height Max firing height in km
|
||||
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
||||
-- @field #string Radar Radar typename on unit level (used as key)
|
||||
-- @field #string Point Point defense capable
|
||||
MANTIS.SamData = {
|
||||
["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km
|
||||
["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS" }, -- AIM 120B
|
||||
@@ -365,16 +382,16 @@ MANTIS.SamData = {
|
||||
["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" },
|
||||
["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"},
|
||||
["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" },
|
||||
["Roland"] = { Range=5, Blindspot=0, Height=5, Type="Short", Radar="Roland" },
|
||||
["Roland"] = { Range=5, Blindspot=0, Height=5, Type="Point", Radar="Roland" },
|
||||
["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" },
|
||||
["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
|
||||
["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Strela", Point="true" },
|
||||
["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" },
|
||||
["SA-19"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Tunguska" },
|
||||
["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Short", Radar="Tor 9A331" },
|
||||
["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
|
||||
["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Point", Radar="Tor 9A331", Point="true" },
|
||||
["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Point", Radar="Strela", Point="true" },
|
||||
["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" },
|
||||
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
|
||||
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" },
|
||||
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" },
|
||||
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
|
||||
-- units from HDS Mod, multi launcher options is tricky
|
||||
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
|
||||
@@ -382,7 +399,6 @@ MANTIS.SamData = {
|
||||
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
|
||||
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
|
||||
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||
["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" },
|
||||
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
|
||||
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
|
||||
}
|
||||
@@ -394,6 +410,7 @@ MANTIS.SamData = {
|
||||
-- @field #number Height Max firing height in km
|
||||
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
||||
-- @field #string Radar Radar typename on unit level (used as key)
|
||||
-- @field #string Point Point defense capable
|
||||
MANTIS.SamDataHDS = {
|
||||
-- units from HDS Mod, multi launcher options is tricky
|
||||
-- group name MUST contain HDS to ID launcher type correctly!
|
||||
@@ -415,20 +432,21 @@ MANTIS.SamDataHDS = {
|
||||
-- @field #number Height Max firing height in km
|
||||
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
||||
-- @field #string Radar Radar typename on unit level (used as key)
|
||||
-- @field #string Point Point defense capable
|
||||
MANTIS.SamDataSMA = {
|
||||
-- units from SMA Mod (Sweedish Military Assets)
|
||||
-- https://forum.dcs.world/topic/295202-swedish-military-assets-for-dcs-by-currenthill/
|
||||
-- group name MUST contain SMA to ID launcher type correctly!
|
||||
["RBS98M SMA"] = { Range=20, Blindspot=0, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-70" },
|
||||
["RBS70M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS70" },
|
||||
["RBS90 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-90" },
|
||||
["RBS90M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS90" },
|
||||
["RBS103A SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M SMA"] = { Range=4, Blindspot=0, Height=2.5, Type="Short", Radar="LvKv9040" },
|
||||
["RBS98M SMA"] = { Range=20, Blindspot=0.2, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-70" },
|
||||
["RBS70M SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS70" },
|
||||
["RBS90 SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-90" },
|
||||
["RBS90M SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS90" },
|
||||
["RBS103A SMA"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B SMA"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM SMA"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM SMA"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M SMA"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
|
||||
}
|
||||
|
||||
--- SAM data CH
|
||||
@@ -438,51 +456,54 @@ MANTIS.SamDataSMA = {
|
||||
-- @field #number Height Max firing height in km
|
||||
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
||||
-- @field #string Radar Radar typename on unit level (used as key)
|
||||
-- @field #string Point Point defense capable
|
||||
MANTIS.SamDataCH = {
|
||||
-- units from CH (Military Assets by Currenthill)
|
||||
-- https://www.currenthill.com/
|
||||
-- group name MUST contain CHM to ID launcher type correctly!
|
||||
["2S38 CHM"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" },
|
||||
["2S38 CHM"] = { Range=6, Blindspot=0.1, Height=4.5, Type="Short", Radar="2S38" },
|
||||
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
|
||||
["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
|
||||
["PGL-625 CHM"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" },
|
||||
["HQ-17A CHM"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||
["M903PAC2 CHM"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||
["M903PAC3 CHM"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||
["PGL-625 CHM"] = { Range=10, Blindspot=1, Height=5, Type="Short", Radar="PGL_625" },
|
||||
["HQ-17A CHM"] = { Range=15, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||
["M903PAC2 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||
["M903PAC3 CHM"] = { Range=160, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
|
||||
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
|
||||
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
|
||||
["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
|
||||
["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
|
||||
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_Centurion_C_RAM" },
|
||||
["PGZ-09 CHM"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="CH_PGZ09" },
|
||||
["S350-9M100 CHM"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
|
||||
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" },
|
||||
["PGZ-09 CHM"] = { Range=4, Blindspot=0.5, Height=3, Type="Point", Radar="CH_PGZ09", Point="true" },
|
||||
["S350-9M100 CHM"] = { Range=15, Blindspot=1, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
|
||||
["S350-9M96D CHM"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" },
|
||||
["LAV-AD CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_LAVAD" },
|
||||
["LAV-AD CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_LAVAD" },
|
||||
["HQ-22 CHM"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" },
|
||||
["PGZ-95 CHM"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_PGZ95" },
|
||||
["LD-3000 CHM"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="CH_LD3000_stationary" },
|
||||
["LD-3000M CHM"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="CH_LD3000" },
|
||||
["FlaRakRad CHM"] = { Range=8, Blindspot=1.5, Height=6, Type="Short", Radar="HQ17A" },
|
||||
["PGZ-95 CHM"] = { Range=2.5, Blindspot=0.5, Height=2, Type="Point", Radar="CH_PGZ95",Point="true" },
|
||||
["LD-3000 CHM"] = { Range=2.5, Blindspot=0.1, Height=3, Type="Point", Radar="CH_LD3000_stationary", Point="true" },
|
||||
["LD-3000M CHM"] = { Range=2.5, Blindspot=0.1, Height=3, Type="Point", Radar="CH_LD3000", Point="true" },
|
||||
["FlaRakRad CHM"] = { Range=8, Blindspot=1.5, Height=6, Type="Short", Radar="CH_FlaRakRad" },
|
||||
["IRIS-T SLM CHM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
|
||||
["M903PAC2KAT1 CHM"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="CH_MIM104_M903_PAC2_KAT1" },
|
||||
["Skynex CHM"] = { Range=3.5, Blindspot=0, Height=3.5, Type="Short", Radar="CH_SkynexHX" },
|
||||
["Skyshield CHM"] = { Range=3.5, Blindspot=0, Height=3.5, Type="Short", Radar="CH_Skyshield_Gun" },
|
||||
["WieselOzelot CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_Wiesel2Ozelot" },
|
||||
["M903PAC2KAT1 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="CH_MIM104_M903_PAC2_KAT1" },
|
||||
["Skynex CHM"] = { Range=3.5, Blindspot=0.1, Height=3.5, Type="Point", Radar="CH_SkynexHX", Point="true" },
|
||||
["Skyshield CHM"] = { Range=3.5, Blindspot=0.1, Height=3.5, Type="Point", Radar="CH_Skyshield_Gun", Point="true" },
|
||||
["WieselOzelot CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_Wiesel2Ozelot" },
|
||||
["BukM3-9M317M CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317M" },
|
||||
["BukM3-9M317MA CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317MA" },
|
||||
["SkySabre CHM"] = { Range=30, Blindspot=0.5, Height=10, Type="Medium", Radar="CH_SkySabreLN" },
|
||||
["Stormer CHM"] = { Range=7.5, Blindspot=0.3, Height=7, Type="Short", Radar="CH_StormerHVM" },
|
||||
["THAAD CHM"] = { Range=200, Blindspot=40, Height=150, Type="Long", Radar="CH_THAAD_M1120" },
|
||||
["USInfantryFIM92K CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_USInfantry_FIM92" },
|
||||
["RBS98M CHM"] = { Range=20, Blindspot=0, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 CHM"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-70" },
|
||||
["RBS90 CHM"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-90" },
|
||||
["RBS103A CHM"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B CHM"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM CHM"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM CHM"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M CHM"] = { Range=4, Blindspot=0, Height=2.5, Type="Short", Radar="LvKv9040" },
|
||||
["USInfantryFIM92K CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_USInfantry_FIM92" },
|
||||
["RBS98M CHM"] = { Range=20, Blindspot=0.2, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-70" },
|
||||
["RBS70M CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS70" },
|
||||
["RBS90 CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-90" },
|
||||
["RBS90M CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS90" },
|
||||
["RBS103A CHM"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM CHM"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M CHM"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
@@ -543,6 +564,7 @@ do
|
||||
self.SAM_Table_Long = {}
|
||||
self.SAM_Table_Medium = {}
|
||||
self.SAM_Table_Short = {}
|
||||
self.SAM_Table_PointDef = {}
|
||||
self.dynamic = dynamic or false
|
||||
self.checkradius = 25000
|
||||
self.grouping = 5000
|
||||
@@ -571,10 +593,6 @@ do
|
||||
self.SuppressedGroups = {}
|
||||
-- 0.8 additions
|
||||
self.automode = true
|
||||
self.radiusscale = {}
|
||||
self.radiusscale[MANTIS.SamType.LONG] = 1.1
|
||||
self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
|
||||
self.radiusscale[MANTIS.SamType.SHORT] = 1.3
|
||||
--self.SAMCheckRanges = {}
|
||||
self.usezones = false
|
||||
self.AcceptZones = {}
|
||||
@@ -583,6 +601,7 @@ do
|
||||
self.maxlongrange = 1
|
||||
self.maxmidrange = 2
|
||||
self.maxshortrange = 2
|
||||
self.maxpointdefrange = 6
|
||||
self.maxclassic = 6
|
||||
self.autoshorad = true
|
||||
self.ShoradGroupSet = SET_GROUP:New() -- Core.Set#SET_GROUP
|
||||
@@ -590,7 +609,10 @@ do
|
||||
|
||||
self.SkateZones = nil
|
||||
self.SkateNumber = 3
|
||||
self.shootandscoot = false
|
||||
self.shootandscoot = false
|
||||
|
||||
self.SmokeDecoy = false
|
||||
self.SmokeDecoyColor = SMOKECOLOR.White
|
||||
|
||||
self.UseEmOnOff = true
|
||||
if EmOnOff == false then
|
||||
@@ -602,6 +624,7 @@ do
|
||||
else
|
||||
self.advAwacs = false
|
||||
end
|
||||
|
||||
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("MANTIS %s | ", self.name)
|
||||
@@ -661,9 +684,12 @@ do
|
||||
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
|
||||
end
|
||||
|
||||
-- counter for SAM table updates
|
||||
self.checkcounter = 1
|
||||
|
||||
-- TODO Version
|
||||
-- @field #string version
|
||||
self.version="0.8.22"
|
||||
self.version="0.9.27"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
--- FSM Functions ---
|
||||
@@ -899,19 +925,31 @@ do
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set Short Range SAMs to spit out smoke as decoy, if an enemy plane is in range.
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean Onoff Set to true for on and nil/false for off.
|
||||
-- @param #number Color (Optional) Color to use, defaults to `SMOKECOLOR.White`
|
||||
function MANTIS:SetSmokeDecoy(Onoff,Color)
|
||||
self.SmokeDecoy = Onoff
|
||||
self.SmokeDecoyColor = Color or SMOKECOLOR.White
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set number of SAMs going active on a valid, detected thread
|
||||
-- @param #MANTIS self
|
||||
-- @param #number Short Number of short-range systems activated, defaults to 1.
|
||||
-- @param #number Mid Number of mid-range systems activated, defaults to 2.
|
||||
-- @param #number Long Number of long-range systems activated, defaults to 2.
|
||||
-- @param #number Classic (non-automode) Number of overall systems activated, defaults to 6.
|
||||
-- @param #number Point Number of point defense and AAA systems activated, defaults to 6.
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetMaxActiveSAMs(Short,Mid,Long,Classic)
|
||||
function MANTIS:SetMaxActiveSAMs(Short,Mid,Long,Classic,Point)
|
||||
self:T(self.lid .. "SetMaxActiveSAMs")
|
||||
self.maxclassic = Classic or 6
|
||||
self.maxlongrange = Long or 1
|
||||
self.maxmidrange = Mid or 2
|
||||
self.maxshortrange = Short or 2
|
||||
self.maxpointdefrange= Point or 6
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1113,6 +1151,24 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Check if any EWR or AWACS is still alive
|
||||
-- @param #MANTIS self
|
||||
-- @return #boolean outcome
|
||||
function MANTIS:_CheckAnyEWRAlive()
|
||||
self:T(self.lid .. "_CheckAnyEWRAlive")
|
||||
local alive = false
|
||||
if self.EWR_Group:CountAlive() > 0 then
|
||||
alive = true
|
||||
end
|
||||
if not alive and self.AWACS_Prefix then
|
||||
local awacs = GROUP:FindByName(self.AWACS_Prefix)
|
||||
if awacs and awacs:IsAlive() then
|
||||
alive = true
|
||||
end
|
||||
end
|
||||
return alive
|
||||
end
|
||||
|
||||
--- [Internal] Function to determine state of the advanced mode
|
||||
-- @param #MANTIS self
|
||||
@@ -1459,6 +1515,17 @@ do
|
||||
end
|
||||
if found then break end
|
||||
end
|
||||
--- AAA or Point Defense
|
||||
if not found then
|
||||
local grp = GROUP:FindByName(grpname)
|
||||
if (grp and grp:IsAlive() and grp:IsAAA()) or string.find(grpname,"AAA",1,true) then
|
||||
range = 2000
|
||||
height = 2000
|
||||
blind = 50
|
||||
type = MANTIS.SamType.POINT
|
||||
found = true
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
|
||||
end
|
||||
@@ -1492,7 +1559,7 @@ do
|
||||
end
|
||||
--if self.automode then
|
||||
for idx,entry in pairs(self.SamData) do
|
||||
self:T("ID = " .. idx)
|
||||
self:T2("ID = " .. idx)
|
||||
if string.find(grpname,idx,1,true) then
|
||||
local _entry = entry -- #MANTIS.SamData
|
||||
type = _entry.Type
|
||||
@@ -1506,14 +1573,25 @@ do
|
||||
end
|
||||
end
|
||||
--end
|
||||
-- secondary filter if not found
|
||||
--- Secondary - AAA or Point Defense
|
||||
if not found then
|
||||
local grp = GROUP:FindByName(grpname)
|
||||
if (grp and grp:IsAlive() and grp:IsAAA()) or string.find(grpname,"AAA",1,true) then
|
||||
range = 2000
|
||||
height = 2000
|
||||
blind = 50
|
||||
type = MANTIS.SamType.POINT
|
||||
found = true
|
||||
end
|
||||
end
|
||||
--- Tertiary filter if not found
|
||||
if (not found) or HDSmod or SMAMod or CHMod then
|
||||
range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod,SMAMod,CHMod)
|
||||
elseif not found then
|
||||
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
|
||||
end
|
||||
if string.find(grpname,"SHORAD",1,true) then
|
||||
type = MANTIS.SamType.SHORT -- force short on match
|
||||
if found and string.find(grpname,"SHORAD",1,true) then
|
||||
type = MANTIS.SamType.POINT -- force short on match
|
||||
end
|
||||
return range, height, type, blind
|
||||
end
|
||||
@@ -1532,6 +1610,7 @@ do
|
||||
local SAM_Tbl_lg = {} -- table of long range SAM defense zones
|
||||
local SAM_Tbl_md = {} -- table of mid range SAM defense zones
|
||||
local SAM_Tbl_sh = {} -- table of short range SAM defense zones
|
||||
local SAM_Tbl_pt = {} -- table of point defense/AAA
|
||||
local SEAD_Grps = {} -- table of SAM names to make evasive
|
||||
local engagerange = self.engagerange -- firing range in % of max
|
||||
--cycle through groups and set alarm state etc
|
||||
@@ -1550,23 +1629,27 @@ do
|
||||
local grpname = group:GetName()
|
||||
local grpcoord = group:GetCoordinate()
|
||||
local grprange,grpheight,type,blind = self:_GetSAMRange(grpname)
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind})
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
--table.insert( SEAD_Grps, grpname )
|
||||
if type == MANTIS.SamType.LONG then
|
||||
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind})
|
||||
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
--self:T("SAM "..grpname.." is type LONG")
|
||||
self:T("SAM "..grpname.." is type LONG")
|
||||
elseif type == MANTIS.SamType.MEDIUM then
|
||||
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind})
|
||||
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
--self:T("SAM "..grpname.." is type MEDIUM")
|
||||
self:T("SAM "..grpname.." is type MEDIUM")
|
||||
elseif type == MANTIS.SamType.SHORT then
|
||||
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind})
|
||||
--self:T("SAM "..grpname.." is type SHORT")
|
||||
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
self:T("SAM "..grpname.." is type SHORT")
|
||||
elseif type == MANTIS.SamType.POINT then
|
||||
table.insert( SAM_Tbl_pt, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
self:T("SAM "..grpname.." is type POINT")
|
||||
self.ShoradGroupSet:Add(grpname,group)
|
||||
if not self.autoshorad then
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
end
|
||||
end
|
||||
end
|
||||
self.SamStateTracker[grpname] = "GREEN"
|
||||
end
|
||||
@@ -1575,6 +1658,7 @@ do
|
||||
self.SAM_Table_Long = SAM_Tbl_lg
|
||||
self.SAM_Table_Medium = SAM_Tbl_md
|
||||
self.SAM_Table_Short = SAM_Tbl_sh
|
||||
self.SAM_Table_PointDef = SAM_Tbl_pt
|
||||
-- make SAMs evasive
|
||||
local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
|
||||
mysead:SetEngagementRange(engagerange)
|
||||
@@ -1598,7 +1682,8 @@ do
|
||||
local SAM_Tbl = {} -- table of SAM defense zones
|
||||
local SAM_Tbl_lg = {} -- table of long range SAM defense zones
|
||||
local SAM_Tbl_md = {} -- table of mid range SAM defense zones
|
||||
local SAM_Tbl_sh = {} -- table of short range SAM defense zon
|
||||
local SAM_Tbl_sh = {} -- table of short range SAM defense zones
|
||||
local SAM_Tbl_pt = {} -- table of point defense/AAA
|
||||
local SEAD_Grps = {} -- table of SAM names to make evasive
|
||||
local engagerange = self.engagerange -- firing range in % of max
|
||||
--cycle through groups and set alarm state etc
|
||||
@@ -1609,17 +1694,21 @@ do
|
||||
local grpname = group:GetName()
|
||||
local grpcoord = group:GetCoordinate()
|
||||
local grprange, grpheight,type,blind = self:_GetSAMRange(grpname)
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind}) -- make the table lighter, as I don't really use the zone here
|
||||
local radaralive = group:IsSAM()
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind, type}) -- make the table lighter, as I don't really use the zone here
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
if type == MANTIS.SamType.LONG then
|
||||
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind})
|
||||
--self:I({grpname,grprange, grpheight})
|
||||
elseif type == MANTIS.SamType.MEDIUM then
|
||||
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind})
|
||||
--self:I({grpname,grprange, grpheight})
|
||||
elseif type == MANTIS.SamType.SHORT then
|
||||
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind})
|
||||
--self:I({grpname,grprange, grpheight})
|
||||
if type == MANTIS.SamType.LONG and radaralive then
|
||||
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
self:T({grpname,grprange, grpheight})
|
||||
elseif type == MANTIS.SamType.MEDIUM and radaralive then
|
||||
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
self:T({grpname,grprange, grpheight})
|
||||
elseif type == MANTIS.SamType.SHORT and radaralive then
|
||||
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
self:T({grpname,grprange, grpheight})
|
||||
elseif type == MANTIS.SamType.POINT or (not radaralive) then
|
||||
table.insert( SAM_Tbl_pt, {grpname, grpcoord, grprange, grpheight, blind, type})
|
||||
self:T({grpname,grprange, grpheight})
|
||||
self.ShoradGroupSet:Add(grpname,group)
|
||||
if self.autoshorad then
|
||||
self.Shorad.Groupset = self.ShoradGroupSet
|
||||
@@ -1631,6 +1720,7 @@ do
|
||||
self.SAM_Table_Long = SAM_Tbl_lg
|
||||
self.SAM_Table_Medium = SAM_Tbl_md
|
||||
self.SAM_Table_Short = SAM_Tbl_sh
|
||||
self.SAM_Table_PointDef = SAM_Tbl_pt
|
||||
-- make SAMs evasive
|
||||
if self.mysead ~= nil then
|
||||
local mysead = self.mysead
|
||||
@@ -1674,7 +1764,9 @@ do
|
||||
-- @param #table detset Table of COORDINATES
|
||||
-- @param #boolean dlink Using DLINK
|
||||
-- @param #number limit of SAM sites to go active on a contact
|
||||
-- @return #MANTIS self
|
||||
-- @return #number instatusred
|
||||
-- @return #number instatusgreen
|
||||
-- @return #number activeshorads
|
||||
function MANTIS:_CheckLoop(samset,detset,dlink,limit)
|
||||
self:T(self.lid .. "CheckLoop " .. #detset .. " Coordinates")
|
||||
local switchedon = 0
|
||||
@@ -1688,6 +1780,10 @@ do
|
||||
local radius = _data[3]
|
||||
local height = _data[4]
|
||||
local blind = _data[5] * 1.25 + 1
|
||||
local shortsam = (_data[6] == MANTIS.SamType.SHORT) and true or false
|
||||
if not shortsam then
|
||||
shortsam = (_data[6] == MANTIS.SamType.POINT) and true or false
|
||||
end
|
||||
local samgroup = GROUP:FindByName(name)
|
||||
local IsInZone, Distance = self:_CheckObjectInZone(detset, samcoordinate, radius, height, dlink)
|
||||
local suppressed = self.SuppressedGroups[name] or false
|
||||
@@ -1713,6 +1809,17 @@ do
|
||||
self:__RedState(1,samgroup)
|
||||
self.SamStateTracker[name] = "RED"
|
||||
end
|
||||
-- TODO doesn't work
|
||||
if shortsam == true and self.SmokeDecoy == true then
|
||||
self:T("Smoking")
|
||||
local units = samgroup:GetUnits() or {}
|
||||
local smoke = self.SmokeDecoyColor or SMOKECOLOR.White
|
||||
for _,unit in pairs(units) do
|
||||
if unit and unit:IsAlive() then
|
||||
unit:GetCoordinate():Smoke(smoke)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- link in to SHORAD if available
|
||||
-- DONE: Test integration fully
|
||||
if self.ShoradLink and (Distance < self.ShoradActDistance or Distance < blind ) then -- don't give SHORAD position away too early
|
||||
@@ -1749,7 +1856,7 @@ do
|
||||
end --end alive
|
||||
end --end check
|
||||
end --for loop
|
||||
if self.debug then
|
||||
if self.debug or self.verbose then
|
||||
for _,_status in pairs(self.SamStateTracker) do
|
||||
if _status == "GREEN" then
|
||||
instatusgreen=instatusgreen+1
|
||||
@@ -1776,22 +1883,24 @@ do
|
||||
--get detected set
|
||||
local detset = detection:GetDetectedItemCoordinates()
|
||||
--self:T("Check:", {detset})
|
||||
-- randomly update SAM Table
|
||||
local rand = math.random(1,100)
|
||||
if rand > 65 then -- 1/3 of cases
|
||||
-- update SAM Table evey 3 runs
|
||||
if self.checkcounter%3 == 0 then
|
||||
self:_RefreshSAMTable()
|
||||
end
|
||||
self.checkcounter = self.checkcounter + 1
|
||||
local instatusred = 0
|
||||
local instatusgreen = 0
|
||||
local activeshorads = 0
|
||||
-- switch SAMs on/off if (n)one of the detected groups is inside their reach
|
||||
if self.automode then
|
||||
local samset = self.SAM_Table_Long -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
||||
self:_CheckLoop(samset,detset,dlink,self.maxlongrange)
|
||||
local instatusredl, instatusgreenl, activeshoradsl = self:_CheckLoop(samset,detset,dlink,self.maxlongrange)
|
||||
local samset = self.SAM_Table_Medium -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
||||
self:_CheckLoop(samset,detset,dlink,self.maxmidrange)
|
||||
local instatusredm, instatusgreenm, activeshoradsm = self:_CheckLoop(samset,detset,dlink,self.maxmidrange)
|
||||
local samset = self.SAM_Table_Short -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
||||
instatusred, instatusgreen, activeshorads = self:_CheckLoop(samset,detset,dlink,self.maxshortrange)
|
||||
local instatusreds, instatusgreens, activeshoradss = self:_CheckLoop(samset,detset,dlink,self.maxshortrange)
|
||||
local samset = self.SAM_Table_PointDef -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
||||
instatusred, instatusgreen, activeshorads = self:_CheckLoop(samset,detset,dlink,self.maxpointdefrange)
|
||||
else
|
||||
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
||||
instatusred, instatusgreen, activeshorads = self:_CheckLoop(samset,detset,dlink,self.maxclassic)
|
||||
@@ -1915,12 +2024,34 @@ do
|
||||
if not self.state2flag then
|
||||
self:_Check(self.Detection,self.DLink)
|
||||
end
|
||||
|
||||
--[[ check Awacs
|
||||
if self.advAwacs and not self.state2flag then
|
||||
self:_Check(self.AWACS_Detection,false)
|
||||
|
||||
local EWRAlive = self:_CheckAnyEWRAlive()
|
||||
|
||||
local function FindSAMSRTR()
|
||||
for i=1,1000 do
|
||||
local randomsam = self.SAM_Group:GetRandom()
|
||||
if randomsam and randomsam:IsAlive() then
|
||||
if randomsam:IsSAM() then return randomsam end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Switch on a random SR/TR if no EWR left over
|
||||
if not EWRAlive then
|
||||
local randomsam = FindSAMSRTR() -- Wrapper.Group#GROUP
|
||||
if randomsam and randomsam:IsAlive() then
|
||||
if self.UseEmOnOff then
|
||||
randomsam:EnableEmission(true)
|
||||
else
|
||||
randomsam:OptionAlarmStateRed()
|
||||
end
|
||||
local name = randomsam:GetName()
|
||||
if self.SamStateTracker[name] ~= "RED" then
|
||||
self:__RedState(1,randomsam)
|
||||
self.SamStateTracker[name] = "RED"
|
||||
end
|
||||
end
|
||||
end
|
||||
--]]
|
||||
|
||||
-- relocate HQ and EWR
|
||||
if self.autorelocate then
|
||||
@@ -1930,8 +2061,6 @@ do
|
||||
|
||||
local halfintv = math.floor(timepassed / relointerval)
|
||||
|
||||
--self:T({timepassed=timepassed, halfintv=halfintv})
|
||||
|
||||
if halfintv >= 1 then
|
||||
self.TimeStamp = timer.getAbsTime()
|
||||
self:_Relocate()
|
||||
@@ -2060,7 +2189,7 @@ do
|
||||
local Shorad = self.Shorad
|
||||
local radius = self.checkradius
|
||||
local ontime = self.ShoradTime
|
||||
Shorad:WakeUpShorad(Name, radius, ontime)
|
||||
Shorad:WakeUpShorad(Name, radius, ontime, nil, true)
|
||||
self:__ShoradActivated(1,Name, radius, ontime)
|
||||
end
|
||||
return self
|
||||
|
||||
@@ -3814,15 +3814,20 @@ function RAT:Status(message, forID)
|
||||
local N0units=group:GetInitialSize()
|
||||
|
||||
-- Monitor travelled distance since last check.
|
||||
local Pnow=coords
|
||||
local Dtravel=Pnow:Get2DDistance(ratcraft.Pnow)
|
||||
ratcraft.Pnow=Pnow
|
||||
local Dtravel=0
|
||||
if coords and ratcraft.Pnow then
|
||||
local Dtravel=coords:Get2DDistance(ratcraft.Pnow)
|
||||
ratcraft.Pnow=coords
|
||||
end
|
||||
|
||||
-- Add up the travelled distance.
|
||||
ratcraft.Distance=ratcraft.Distance+Dtravel
|
||||
|
||||
-- Distance remaining to destination.
|
||||
local Ddestination=Pnow:Get2DDistance(ratcraft.destination:GetCoordinate())
|
||||
local Ddestination=-1
|
||||
if ratcraft.Pnow then
|
||||
Ddestination=ratcraft.Pnow:Get2DDistance(ratcraft.destination:GetCoordinate())
|
||||
end
|
||||
|
||||
-- Status report.
|
||||
if (forID and spawnindex==forID) or (not forID) then
|
||||
|
||||
@@ -107,6 +107,9 @@
|
||||
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
|
||||
-- @field #number Coalition Coalition side for the menu, if any.
|
||||
-- @field Core.Menu#MENU_MISSION menuF10root Specific user defined root F10 menu.
|
||||
-- @field #number ceilingaltitude Range ceiling altitude in ft MSL. Aircraft above this altitude are not considered to be in the range. Default is 20000 ft.
|
||||
-- @field #boolean ceilingenabled Range has a ceiling and is not unlimited. Default is false.
|
||||
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
|
||||
@@ -273,6 +276,10 @@
|
||||
-- -- Create a range object.
|
||||
-- GoldwaterRange=RANGE:New("Goldwater Range")
|
||||
--
|
||||
-- -- Set and enable the range ceiling altitude in feet MSL. If aircraft are above this altitude they are not considered to be in the range.
|
||||
-- GoldwaterRange:SetRangeCeiling(20000)
|
||||
-- GoldwaterRange:EnableRangeCeiling(true)
|
||||
--
|
||||
-- -- Distance between strafe target and foul line. You have to specify the names of the unit or static objects.
|
||||
-- -- Note that this could also be done manually by simply measuring the distance between the target and the foul line in the ME.
|
||||
-- GoldwaterRange:GetFoullineDistance("GWR Strafe Pit Left 1", "GWR Foul Line Left")
|
||||
@@ -358,6 +365,8 @@ RANGE = {
|
||||
targetpath = nil,
|
||||
targetprefix = nil,
|
||||
Coalition = nil,
|
||||
ceilingaltitude = 20000,
|
||||
ceilingenabled = false,
|
||||
}
|
||||
|
||||
--- Default range parameters.
|
||||
@@ -1085,6 +1094,37 @@ function RANGE:SetRangeZone( zone )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set range ceiling altitude in feet MSL.
|
||||
-- @param #RANGE self
|
||||
-- @param #number altitude (optional) Ceiling altitude of the range in ft MSL. Default 20000ft MSL
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetRangeCeiling( altitude )
|
||||
self:T(self.lid.."SetRangeCeiling")
|
||||
if altitude and type(altitude) == "number" then
|
||||
self.ceilingaltitude=altitude
|
||||
else
|
||||
self:E(self.lid.."Altitude either not provided or is not a number, using default setting (20000).")
|
||||
self.ceilingaltitude=20000
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable range ceiling. Aircraft must be below the ceiling altitude to be considered in the range zone.
|
||||
-- @param #RANGE self
|
||||
-- @param #boolean enabled True if you would like to enable the ceiling check. If no value give, will Default to false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:EnableRangeCeiling( enabled )
|
||||
self:T(self.lid.."EnableRangeCeiling")
|
||||
if enabled and type(enabled) == "boolean" then
|
||||
self.ceilingenabled=enabled
|
||||
else
|
||||
self:E(self.lid.."Enabled either not provide or is not a boolean, using default setting (false).")
|
||||
self.ceilingenabled=false
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set smoke color for marking bomb targets. By default bomb targets are marked by red smoke.
|
||||
-- @param #RANGE self
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.Red`.
|
||||
@@ -1893,7 +1933,7 @@ function RANGE:OnEventHit( EventData )
|
||||
local _currentTarget = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
|
||||
|
||||
-- Player has rolled in on a strafing target.
|
||||
if _currentTarget and target:IsAlive() then
|
||||
if _currentTarget and target and target:IsAlive() then
|
||||
|
||||
local playerPos = _unit:GetCoordinate()
|
||||
local targetPos = target:GetCoordinate()
|
||||
@@ -3112,7 +3152,10 @@ function RANGE:_CheckPlayers()
|
||||
|
||||
if unit and unit:IsAlive() then
|
||||
|
||||
if unit:IsInZone( self.rangezone ) then
|
||||
local unitalt = unit:GetAltitude(false)
|
||||
local unitaltinfeet = UTILS.MetersToFeet(unitalt)
|
||||
|
||||
if unit:IsInZone(self.rangezone) and (not self.ceilingenabled or unitaltinfeet < self.ceilingaltitude) then
|
||||
|
||||
------------------------------
|
||||
-- Player INSIDE Range Zone --
|
||||
|
||||
@@ -113,7 +113,7 @@ SHORAD = {
|
||||
SkateNumber = 3,
|
||||
SkateZones = nil,
|
||||
minscootdist = 100,
|
||||
minscootdist = 3000,
|
||||
maxscootdist = 3000,
|
||||
scootrandomcoord = false,
|
||||
}
|
||||
|
||||
@@ -443,7 +443,9 @@ do
|
||||
for _,_groups in pairs (shoradset) do
|
||||
local groupname = _groups:GetName()
|
||||
if string.find(groupname, tgtgrp, 1, true) then
|
||||
returnname = true
|
||||
if _groups:IsSAM() then
|
||||
returnname = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return returnname
|
||||
@@ -470,6 +472,7 @@ do
|
||||
-- @param #number Radius Radius of the #ZONE
|
||||
-- @param #number ActiveTimer Number of seconds to stay active
|
||||
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
|
||||
-- @param #boolean ShotAt If true, function is called after a shot
|
||||
-- @return #SHORAD self
|
||||
-- @usage Use this function to integrate with other systems, example
|
||||
--
|
||||
@@ -479,7 +482,7 @@ do
|
||||
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
|
||||
-- mymantis:AddShorad(myshorad,720)
|
||||
-- mymantis:Start()
|
||||
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat)
|
||||
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat, ShotAt)
|
||||
self:T(self.lid .. " WakeUpShorad")
|
||||
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||
local targetcat = TargetCat or Object.Category.UNIT
|
||||
@@ -524,7 +527,7 @@ do
|
||||
|
||||
local groupname = _group:GetName()
|
||||
|
||||
if groupname == TargetGroup then
|
||||
if groupname == TargetGroup and ShotAt==true then
|
||||
-- Shot at a SHORAD group
|
||||
if self.UseEmOnOff then
|
||||
_group:EnableEmission(false)
|
||||
@@ -540,7 +543,7 @@ do
|
||||
self:__ShootAndScoot(1,_group)
|
||||
end
|
||||
|
||||
elseif _group:IsAnyInZone(targetzone) then
|
||||
elseif _group:IsAnyInZone(targetzone) or groupname == TargetGroup then
|
||||
-- shot at a group we protect
|
||||
local text = string.format("Waking up SHORAD %s", _group:GetName())
|
||||
self:T(text)
|
||||
@@ -626,7 +629,7 @@ do
|
||||
_targetgroupname = tgtgrp:GetName() -- group name
|
||||
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||
self:T("*** Found Target = ".. _targetgroupname)
|
||||
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
|
||||
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -755,7 +758,7 @@ do
|
||||
-- if being shot at, find closest SHORADs to activate
|
||||
if shotatsams or shotatus then
|
||||
self:T({shotatsams=shotatsams,shotatus=shotatus})
|
||||
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
|
||||
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -288,7 +288,7 @@ function TIRESIAS:_InitGroups()
|
||||
}
|
||||
end
|
||||
if grp.Tiresias and (not grp.Tiresias.exception == true) then
|
||||
if grp.Tiresias.invisible and grp.Tiresias.invisible == false then
|
||||
if grp.Tiresias.invisible == false then
|
||||
grp:SetCommandInvisible(true)
|
||||
grp.Tiresias.invisible = true
|
||||
if SwitchAAA then
|
||||
@@ -315,10 +315,11 @@ function TIRESIAS:_InitGroups()
|
||||
}
|
||||
end
|
||||
if grp.Tiresias and (not grp.Tiresias.exception == true) then
|
||||
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
|
||||
if grp.Tiresias and grp.Tiresias.invisible == false then
|
||||
grp:SetCommandInvisible(true)
|
||||
grp:SetAIOff()
|
||||
grp.Tiresias.invisible = true
|
||||
grp.Tiresias.AIOff = true
|
||||
end
|
||||
end
|
||||
--BASE:I(string.format("Init/Switch off Vehicle %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
|
||||
@@ -336,7 +337,7 @@ function TIRESIAS:_InitGroups()
|
||||
}
|
||||
end
|
||||
if grp.Tiresias and (not grp.Tiresias.exception == true) then
|
||||
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
|
||||
if grp.Tiresias and grp.Tiresias.invisible == false then
|
||||
grp:SetCommandInvisible(true)
|
||||
grp.Tiresias.invisible = true
|
||||
end
|
||||
@@ -392,7 +393,8 @@ function TIRESIAS:_SwitchOnGroups(group,radius)
|
||||
ground:ForEachGroupAlive(
|
||||
function(grp)
|
||||
local name = grp:GetName()
|
||||
if grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then
|
||||
if grp:GetCoalition() ~= group:GetCoalition()
|
||||
and grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then
|
||||
if grp.Tiresias.invisible == true then
|
||||
grp:SetCommandInvisible(false)
|
||||
grp.Tiresias.invisible = false
|
||||
|
||||
@@ -6047,7 +6047,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
|
||||
|
||||
else
|
||||
|
||||
if #parking<#template.units and not airstart then
|
||||
if parking and #parking<#template.units and not airstart then
|
||||
local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units)
|
||||
self:_DebugMessage(text)
|
||||
return nil
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Enums.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Utils.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Profiler.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Templates.lua' )
|
||||
--__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/STTS.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/FiFo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Socket.lua' )
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
-- * Option to present information in imperial or metric units
|
||||
-- * Runway length and airfield elevation (optional)
|
||||
-- * Frequencies/channels of nav aids (ILS, VOR, NDB, TACAN, PRMG, RSBN) (optional)
|
||||
-- * SRS Simple-Text-To-Speech (STTS) integration (no sound files necessary)
|
||||
-- * SRS Simple-Text-To-Speech (MSRS) integration (no sound files necessary)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -2049,12 +2049,14 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
local sunrise = coord:GetSunrise()
|
||||
--self:I(sunrise)
|
||||
local SUNRISE = "no time"
|
||||
local NorthPolar = true
|
||||
if tostring(sunrise) ~= "N/S" and tostring(sunrise) ~= "N/R" then
|
||||
sunrise = UTILS.Split( sunrise, ":" )
|
||||
SUNRISE = string.format( "%s%s", sunrise[1], sunrise[2] )
|
||||
if self.useSRS then
|
||||
SUNRISE = string.format( "%s %s %s", sunrise[1], sunrise[2], hours )
|
||||
end
|
||||
NorthPolar = false
|
||||
end
|
||||
|
||||
local sunset = coord:GetSunset()
|
||||
@@ -2066,6 +2068,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
if self.useSRS then
|
||||
SUNSET = string.format( "%s %s %s", sunset[1], sunset[2], hours )
|
||||
end
|
||||
NorthPolar = false
|
||||
end
|
||||
|
||||
---------------------------------
|
||||
@@ -2405,7 +2408,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
local sunrise = self.gettext:GetEntry("SUNRISEAT",self.locale)
|
||||
--subtitle = string.format( "Sunrise at %s local time", SUNRISE )
|
||||
subtitle = string.format( sunrise, SUNRISE )
|
||||
if not self.useSRS then
|
||||
if not self.useSRS and NorthPolar == false then
|
||||
self:Transmission( self.Sound.SunriseAt, 0.5, subtitle )
|
||||
self.radioqueue:Number2Transmission( SUNRISE, nil, 0.2 )
|
||||
self:Transmission( self.Sound.TimeLocal, 0.2 )
|
||||
@@ -2416,7 +2419,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
local sunset = self.gettext:GetEntry("SUNSETAT",self.locale)
|
||||
--subtitle = string.format( "Sunset at %s local time", SUNSET )
|
||||
subtitle = string.format( sunset, SUNSET )
|
||||
if not self.useSRS then
|
||||
if not self.useSRS and NorthPolar == false then
|
||||
self:Transmission( self.Sound.SunsetAt, 0.5, subtitle )
|
||||
self.radioqueue:Number2Transmission( SUNSET, nil, 0.5 )
|
||||
self:Transmission( self.Sound.TimeLocal, 0.2 )
|
||||
|
||||
@@ -1719,15 +1719,16 @@ end
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object.
|
||||
-- @param #number Altitude Engage altitude in feet. Default 2000 ft.
|
||||
-- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewSTRIKE(Target, Altitude)
|
||||
function AUFTRAG:NewSTRIKE(Target, Altitude, EngageWeaponType)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE)
|
||||
|
||||
mission:_TargetFromObject(Target)
|
||||
|
||||
-- DCS Task options:
|
||||
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponType=EngageWeaponType or ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
|
||||
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000)
|
||||
|
||||
@@ -1750,15 +1751,16 @@ end
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object.
|
||||
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
|
||||
-- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewBOMBING(Target, Altitude)
|
||||
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
|
||||
|
||||
mission:_TargetFromObject(Target)
|
||||
|
||||
-- DCS task options:
|
||||
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponType=EngageWeaponType or ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
|
||||
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
|
||||
|
||||
@@ -2156,7 +2158,11 @@ end
|
||||
|
||||
]]
|
||||
|
||||
--- **[GROUND, NAVAL]** Create an ARTY mission.
|
||||
--- **[GROUND, NAVAL]** Create an ARTY mission ("Fire at point" task).
|
||||
--
|
||||
-- If the group has more than one weapon type supporting the "Fire at point" task, the employed weapon type can be set via the `AUFTRAG:SetWeaponType()` function.
|
||||
--
|
||||
-- **Note** that it is recommended to set the weapon range via the `OPSGROUP:AddWeaponRange()` function as this cannot be retrieved from the DCS API.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Target Center of the firing solution.
|
||||
-- @param #number Nshots Number of shots to be fired. Default `#nil`.
|
||||
@@ -2179,6 +2185,7 @@ function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude)
|
||||
mission.optionAlarm=0
|
||||
|
||||
mission.missionFraction=0.0
|
||||
mission.missionWaypointRadius=0.0
|
||||
|
||||
-- Evaluate after 8 min.
|
||||
mission.dTevaluate=8*60
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **applevangelist**
|
||||
-- @date Last Update Dec 2024
|
||||
-- @date Last Update Jan 2025
|
||||
-- @module Ops.AWACS
|
||||
-- @image OPS_AWACS.jpg
|
||||
|
||||
@@ -184,7 +184,7 @@ do
|
||||
--
|
||||
-- Add Escorts Squad (recommended, optional)
|
||||
--
|
||||
-- local Squad_Two = SQUADRON:New("Escorts",4,"Escorts North")
|
||||
-- local Squad_Two = SQUADRON:New("Escorts",4,"Escorts North") -- taking a template with 2 planes here, will result in a group of 2 escorts which can fly in formation escorting the AWACS.
|
||||
-- Squad_Two:AddMissionCapability({AUFTRAG.Type.ESCORT})
|
||||
-- Squad_Two:SetFuelLowRefuel(true)
|
||||
-- Squad_Two:SetFuelLowThreshold(0.3)
|
||||
@@ -232,8 +232,8 @@ do
|
||||
-- -- set up in the mission editor with a late activated helo named "Rock#ZONE_POLYGON". Note this also sets the BullsEye to be referenced as "Rock".
|
||||
-- -- The CAP station zone is called "Fremont". We will be on 255 AM.
|
||||
-- local testawacs = AWACS:New("AWACS North",AwacsAW,"blue",AIRBASE.Caucasus.Kutaisi,"Awacs Orbit",ZONE:FindByName("Rock"),"Fremont",255,radio.modulation.AM )
|
||||
-- -- set two escorts
|
||||
-- testawacs:SetEscort(2)
|
||||
-- -- set one escort group; this example has two units in the template group, so they can fly a nice formation.
|
||||
-- testawacs:SetEscort(1,ENUMS.Formation.FixedWing.FingerFour.Group,{x=-500,y=50,z=500},45)
|
||||
-- -- Callsign will be "Focus". We'll be a Angels 30, doing 300 knots, orbit leg to 88deg with a length of 25nm.
|
||||
-- testawacs:SetAwacsDetails(CALLSIGN.AWACS.Focus,1,30,300,88,25)
|
||||
-- -- Set up SRS on port 5010 - change the below to your path and port
|
||||
@@ -509,7 +509,7 @@ do
|
||||
-- @field #AWACS
|
||||
AWACS = {
|
||||
ClassName = "AWACS", -- #string
|
||||
version = "0.2.68", -- #string
|
||||
version = "0.2.71", -- #string
|
||||
lid = "", -- #string
|
||||
coalition = coalition.side.BLUE, -- #number
|
||||
coalitiontxt = "blue", -- #string
|
||||
@@ -1773,7 +1773,7 @@ function AWACS:_EventHandler(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
if Event.id == EVENTS.PlayerLeaveUnit then --player left unit
|
||||
if Event.id == EVENTS.PlayerLeaveUnit and Event.IniGroupName then --player left unit
|
||||
-- check known player?
|
||||
self:T("Player group left unit: " .. Event.IniGroupName)
|
||||
self:T("Player name left: " .. Event.IniPlayerName)
|
||||
@@ -2169,9 +2169,12 @@ end
|
||||
|
||||
--- [User] Set AWACS Escorts Template
|
||||
-- @param #AWACS self
|
||||
-- @param #number EscortNumber Number of fighther planes to accompany this AWACS. 0 or nil means no escorts.
|
||||
-- @param #number EscortNumber Number of fighther plane GROUPs to accompany this AWACS. 0 or nil means no escorts. If you want >1 plane in an escort group, you can either set the respective squadron grouping to the desired number, or use a template for escorts with >1 unit.
|
||||
-- @param #number Formation Formation the escort should take (if more than one plane), e.g. `ENUMS.Formation.FixedWing.FingerFour.Group`. Formation is used on GROUP level, multiple groups of one unit will NOT conform to this formation.
|
||||
-- @param #table OffsetVector Offset the escorts should fly behind the AWACS, given as table, distance in meters, e.g. `{x=-500,y=0,z=500}` - 500m behind (negative value) and to the right (negative for left), no vertical separation (positive over, negative under the AWACS flight). For multiple groups, the vectors will be slightly changed to avoid collisions.
|
||||
-- @param #number EscortEngageMaxDistance Escorts engage air targets max this NM away, defaults to 45NM.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetEscort(EscortNumber)
|
||||
function AWACS:SetEscort(EscortNumber,Formation,OffsetVector,EscortEngageMaxDistance)
|
||||
self:T(self.lid.."SetEscort")
|
||||
if EscortNumber and EscortNumber > 0 then
|
||||
self.HasEscorts = true
|
||||
@@ -2180,6 +2183,9 @@ function AWACS:SetEscort(EscortNumber)
|
||||
self.HasEscorts = false
|
||||
self.EscortNumber = 0
|
||||
end
|
||||
self.EscortFormation = Formation
|
||||
self.OffsetVec = OffsetVector or {x=500,y=100,z=500}
|
||||
self.EscortEngageMaxDistance = EscortEngageMaxDistance or 45
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2234,12 +2240,26 @@ function AWACS:_StartEscorts(Shiftchange)
|
||||
local group = AwacsFG:GetGroup()
|
||||
|
||||
local timeonstation = (self.EscortsTimeOnStation + self.ShiftChangeTime) * 3600 -- hours to seconds
|
||||
local OffsetX = 500
|
||||
local OffsetY = 500
|
||||
local OffsetZ = 500
|
||||
if self.OffsetVec then
|
||||
OffsetX = self.OffsetVec.x or 500
|
||||
OffsetY = self.OffsetVec.y or 500
|
||||
OffsetZ = self.OffsetVec.z or 500
|
||||
end
|
||||
|
||||
for i=1,self.EscortNumber do
|
||||
-- every
|
||||
local escort = AUFTRAG:NewESCORT(group, {x= -100*((i + (i%2))/2), y=0, z=(100 + 100*((i + (i%2))/2))*(-1)^i},45,{"Air"})
|
||||
escort:SetRequiredAssets(1)
|
||||
-- every
|
||||
local escort = AUFTRAG:NewESCORT(group, {x= OffsetX*((i + (i%2))/2), y=OffsetY*((i + (i%2))/2), z=(OffsetZ + OffsetZ*((i + (i%2))/2))*(-1)^i},self.EscortEngageMaxDistance,{"Air"})
|
||||
--local escort = AUFTRAG:NewESCORT(group,self.OffsetVec,self.EscortEngageMaxDistance,{"Air"})
|
||||
--escort:SetRequiredAssets(self.EscortNumber)
|
||||
escort:SetTime(nil,timeonstation)
|
||||
if self.Escortformation then
|
||||
escort:SetFormation(self.Escortformation)
|
||||
end
|
||||
escort:SetMissionRange(self.MaxMissionRange)
|
||||
|
||||
self.AirWing:AddMission(escort)
|
||||
self.CatchAllMissions[#self.CatchAllMissions+1] = escort
|
||||
|
||||
@@ -3642,7 +3662,7 @@ function AWACS:_CheckIn(Group)
|
||||
managedgroup.LastTasking = timer.getTime()
|
||||
|
||||
GID = managedgroup.GID
|
||||
self.ManagedGrps[self.ManagedGrpID]=managedgroup
|
||||
self.ManagedGrps[self.ManagedGrpID]=managedgroup
|
||||
|
||||
local alphacheckbulls = self:_ToStringBULLS(Group:GetCoordinate())
|
||||
local alphacheckbullstts = self:_ToStringBULLS(Group:GetCoordinate(),false,true)
|
||||
@@ -3909,6 +3929,12 @@ function AWACS:_SetClientMenus()
|
||||
checkin = checkin,
|
||||
}
|
||||
self.clientmenus:Push(menus,cgrpname)
|
||||
-- catch errors - when this entry is built we should NOT have a managed entry
|
||||
local GID,hasentry = self:_GetManagedGrpID(cgrp)
|
||||
if hasentry then
|
||||
-- this user is checked in but has the check in entry ... not good.
|
||||
self:_CheckOut(cgrp,GID,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -6065,6 +6091,7 @@ function AWACS:_CheckAwacsStatus()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------
|
||||
-- AWACS
|
||||
--------------------------------
|
||||
@@ -6213,12 +6240,13 @@ function AWACS:_CheckAwacsStatus()
|
||||
|
||||
report:Add("====================")
|
||||
|
||||
local RESMission
|
||||
-- Check for replacement mission - if any
|
||||
if self.ShiftChangeEscortsFlag and self.ShiftChangeEscortsRequested then -- Ops.Auftrag#AUFTRAG
|
||||
ESmission = self.EscortMissionReplacement[i]
|
||||
local esstatus = ESmission:GetState()
|
||||
local ESmissiontime = (timer.getTime() - self.EscortsTimeStamp)
|
||||
local ESTOSLeft = UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600) - ESmissiontime),0) -- seconds
|
||||
RESMission = self.EscortMissionReplacement[i]
|
||||
local esstatus = RESMission:GetState()
|
||||
local RESMissiontime = (timer.getTime() - self.EscortsTimeStamp)
|
||||
local ESTOSLeft = UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600) - RESMissiontime),0) -- seconds
|
||||
ESTOSLeft = UTILS.Round(ESTOSLeft/60,0) -- minutes
|
||||
local ChangeTime = UTILS.Round(((self.ShiftChangeTime * 3600)/60),0)
|
||||
|
||||
@@ -6226,7 +6254,7 @@ function AWACS:_CheckAwacsStatus()
|
||||
report:Add(string.format("Auftrag Status: %s",esstatus))
|
||||
report:Add(string.format("TOS Left: %d min",ESTOSLeft))
|
||||
|
||||
local OpsGroups = ESmission:GetOpsGroups()
|
||||
local OpsGroups = RESMission:GetOpsGroups()
|
||||
local OpsGroup = self:_GetAliveOpsGroupFromTable(OpsGroups) -- Ops.OpsGroup#OPSGROUP
|
||||
if OpsGroup then
|
||||
local OpsName = OpsGroup:GetName() or "Unknown"
|
||||
@@ -6238,13 +6266,13 @@ function AWACS:_CheckAwacsStatus()
|
||||
report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
|
||||
end
|
||||
|
||||
if ESmission:IsExecuting() then
|
||||
if RESMission and RESMission:IsExecuting() then
|
||||
-- make the actual change in the queue
|
||||
self.ShiftChangeEscortsFlag = false
|
||||
self.ShiftChangeEscortsRequested = false
|
||||
-- cancel old mission
|
||||
if ESmission and ESmission:IsNotOver() then
|
||||
ESmission:Cancel()
|
||||
ESmission:__Cancel(1)
|
||||
end
|
||||
self.EscortMission[i] = self.EscortMissionReplacement[i]
|
||||
self.EscortMissionReplacement[i] = nil
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
-- @image OPS_CSAR.jpg
|
||||
|
||||
---
|
||||
-- Last Update Sep 2024
|
||||
-- Last Update Jan 2025
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
|
||||
@@ -92,7 +92,7 @@
|
||||
-- mycsar.immortalcrew = true -- Set to true to make wounded crew immortal.
|
||||
-- mycsar.invisiblecrew = false -- Set to true to make wounded crew insvisible.
|
||||
-- mycsar.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters.
|
||||
-- mycsar.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes.
|
||||
-- mycsar.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes. Will also try to add ZONE and STATIC objects with this prefix once at startup.
|
||||
-- mycsar.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined.
|
||||
-- mycsar.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages.
|
||||
-- mycsar.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons.
|
||||
@@ -313,7 +313,7 @@ CSAR.AircraftType["CH-47Fbl1"] = 31
|
||||
|
||||
--- CSAR class version.
|
||||
-- @field #string version
|
||||
CSAR.version="1.0.29"
|
||||
CSAR.version="1.0.30"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -809,6 +809,8 @@ end
|
||||
-- @param #boolean noMessage
|
||||
-- @param #string _description Description
|
||||
-- @param #boolean forcedesc Use the description only for the pilot track entry
|
||||
-- @return Wrapper.Group#GROUP PilotInField Pilot GROUP object
|
||||
-- @return #string AliasName Alias display name
|
||||
function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description, forcedesc )
|
||||
self:T(self.lid .. " _AddCsar")
|
||||
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
|
||||
@@ -878,7 +880,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
||||
|
||||
self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage, _playerName) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
|
||||
|
||||
return self
|
||||
return _spawnedGroup, _alias
|
||||
end
|
||||
|
||||
--- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
|
||||
@@ -1829,8 +1831,9 @@ end
|
||||
--- (Internal) Function to get string of a group\'s position.
|
||||
-- @param #CSAR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE _woundedGroup Group or Unit object.
|
||||
-- @param Wrapper.Unit#UNIT _Unit Requesting helo pilot unit
|
||||
-- @return #string Coordinates as Text
|
||||
function CSAR:_GetPositionOfWounded(_woundedGroup)
|
||||
function CSAR:_GetPositionOfWounded(_woundedGroup,_Unit)
|
||||
self:T(self.lid .. " _GetPositionOfWounded")
|
||||
local _coordinate = _woundedGroup:GetCoordinate()
|
||||
local _coordinatesText = "None"
|
||||
@@ -1845,6 +1848,26 @@ function CSAR:_GetPositionOfWounded(_woundedGroup)
|
||||
_coordinatesText = _coordinate:ToStringBULLS(self.coalition)
|
||||
end
|
||||
end
|
||||
if _Unit and _Unit:GetPlayerName() then
|
||||
local playername = _Unit:GetPlayerName()
|
||||
if playername then
|
||||
local settings = _DATABASE:GetPlayerSettings(playername) or _SETTINGS
|
||||
if settings then
|
||||
self:T("Get Settings ok!")
|
||||
if settings:IsA2G_MGRS() then
|
||||
_coordinatesText = _coordinate:ToStringMGRS(settings)
|
||||
elseif settings:IsA2G_LL_DMS() then
|
||||
_coordinatesText = _coordinate:ToStringLLDMS(settings)
|
||||
elseif settings:IsA2G_LL_DDM() then
|
||||
_coordinatesText = _coordinate:ToStringLLDDM(settings)
|
||||
elseif settings:IsA2G_BR() then
|
||||
-- attention this is the distance from the ASKING unit to target, not from RECCE to target!
|
||||
local startcoordinate = _Unit:GetCoordinate()
|
||||
_coordinatesText = _coordinate:ToStringBR(startcoordinate,settings)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return _coordinatesText
|
||||
end
|
||||
|
||||
@@ -1870,13 +1893,17 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
||||
self:T({Table=_value})
|
||||
local _woundedGroup = _value.group
|
||||
if _woundedGroup and _value.alive then
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup,_heli)
|
||||
local _helicoord = _heli:GetCoordinate()
|
||||
local _woundcoord = _woundedGroup:GetCoordinate()
|
||||
local _distance = self:_GetDistance(_helicoord, _woundcoord)
|
||||
self:T({_distance = _distance})
|
||||
local distancetext = ""
|
||||
if _SETTINGS:IsImperial() then
|
||||
local settings = _SETTINGS
|
||||
if _heli:GetPlayerName() then
|
||||
settings = _DATABASE:GetPlayerSettings(_heli:GetPlayerName()) or _SETTINGS
|
||||
end
|
||||
if settings:IsImperial() then
|
||||
distancetext = string.format("%.1fnm",UTILS.MetersToNM(_distance))
|
||||
else
|
||||
distancetext = string.format("%.1fkm", _distance/1000.0)
|
||||
@@ -2425,7 +2452,22 @@ function CSAR:onafterStart(From, Event, To)
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
|
||||
end
|
||||
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart()
|
||||
|
||||
local staticmashes = SET_STATIC:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterOnce()
|
||||
local zonemashes = SET_ZONE:New():FilterPrefixes(self.mashprefix):FilterOnce()
|
||||
|
||||
if staticmashes:Count() > 0 then
|
||||
for _,_mash in pairs(staticmashes.Set) do
|
||||
self.mash:AddObject(_mash)
|
||||
end
|
||||
end
|
||||
|
||||
if zonemashes:Count() > 0 then
|
||||
for _,_mash in pairs(zonemashes.Set) do
|
||||
self.mash:AddObject(_mash)
|
||||
end
|
||||
end
|
||||
|
||||
if not self.coordinate then
|
||||
local csarhq = self.mash:GetRandom()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1774,8 +1774,10 @@ function COMMANDER:RecruitAssetsForMission(Mission)
|
||||
MaxWeight=cohort.cargobayLimit
|
||||
end
|
||||
end
|
||||
|
||||
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
|
||||
|
||||
if MaxWeight then
|
||||
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
|
||||
end
|
||||
end
|
||||
|
||||
local legions=self.legions
|
||||
@@ -2165,4 +2167,4 @@ end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -252,7 +252,7 @@ EASYGCICAP = {
|
||||
|
||||
--- EASYGCICAP class version.
|
||||
-- @field #string version
|
||||
EASYGCICAP.version="0.1.16"
|
||||
EASYGCICAP.version="0.1.17"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -348,9 +348,23 @@ function EASYGCICAP:SetTankerAndAWACSInvisible(Switch)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set Maximum of alive missions to stop airplanes spamming the map
|
||||
--- Count alive missions in our internal stack.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Cap-Missions + Intercept-Missions + Alert5-Missionsm default is 6
|
||||
-- @return #number count
|
||||
function EASYGCICAP:_CountAliveAuftrags()
|
||||
local alive = 0
|
||||
for _,_auftrag in pairs(self.ListOfAuftrag) do
|
||||
local auftrag = _auftrag -- Ops.Auftrag#AUFTRAG
|
||||
if auftrag and (not (auftrag:IsCancelled() or auftrag:IsDone() or auftrag:IsOver())) then
|
||||
alive = alive + 1
|
||||
end
|
||||
end
|
||||
return alive
|
||||
end
|
||||
|
||||
--- Set Maximum of alive missions created by this instance to stop airplanes spamming the map
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Intercept-Missions + Alert5-Missions, default is 8
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetMaxAliveMissions(Maxiumum)
|
||||
self:T(self.lid.."SetMaxAliveMissions")
|
||||
@@ -585,7 +599,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
|
||||
local TankerInvisible = self.TankerInvisible
|
||||
|
||||
function CAP_Wing:OnAfterFlightOnMission(From, Event, To, Flightgroup, Mission)
|
||||
function CAP_Wing:onbeforeFlightOnMission(From, Event, To, Flightgroup, Mission)
|
||||
local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP
|
||||
if DespawnAfterLanding then
|
||||
flightgroup:SetDespawnAfterLanding()
|
||||
@@ -615,7 +629,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
flightgroup:SetFuelLowRTB(true)
|
||||
Intel:AddAgent(flightgroup)
|
||||
if DespawnAfterHolding then
|
||||
function flightgroup:OnAfterHolding(From,Event,To)
|
||||
function flightgroup:onbeforeHolding(From,Event,To)
|
||||
self:Despawn(1,true)
|
||||
end
|
||||
end
|
||||
@@ -1177,7 +1191,7 @@ function EASYGCICAP:_AssignIntercept(Cluster)
|
||||
|
||||
local wings = self.wings
|
||||
local ctlpts = self.ManagedCP
|
||||
local MaxAliveMissions = self.MaxAliveMissions * self.capgrouping
|
||||
local MaxAliveMissions = self.MaxAliveMissions --* self.capgrouping
|
||||
local nogozoneset = self.NoGoZoneSet
|
||||
local ReadyFlightGroups = self.ReadyFlightGroups
|
||||
|
||||
@@ -1242,9 +1256,10 @@ function EASYGCICAP:_AssignIntercept(Cluster)
|
||||
-- Do we have a matching airwing?
|
||||
if targetairwing then
|
||||
local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort)
|
||||
local missioncount = self:_CountAliveAuftrags()
|
||||
-- Enough airframes on mission already?
|
||||
self:T(self.lid.." Assets on Mission "..AssetCount)
|
||||
if AssetCount <= MaxAliveMissions then
|
||||
if missioncount < MaxAliveMissions then
|
||||
local repeats = repeatsonfailure
|
||||
local InterceptAuftrag = AUFTRAG:NewINTERCEPT(contact.group)
|
||||
:SetMissionRange(150)
|
||||
@@ -1312,7 +1327,7 @@ function EASYGCICAP:_StartIntel()
|
||||
self:_AssignIntercept(Cluster)
|
||||
end
|
||||
|
||||
function BlueIntel:OnAfterNewCluster(From,Event,To,Cluster)
|
||||
function BlueIntel:onbeforeNewCluster(From,Event,To,Cluster)
|
||||
AssignCluster(Cluster)
|
||||
end
|
||||
|
||||
@@ -1429,12 +1444,14 @@ function EASYGCICAP:onafterStatus(From,Event,To)
|
||||
local text = "GCICAP "..self.alias
|
||||
text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nCapPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock
|
||||
text = text.."\nThreats: "..threatcount
|
||||
text = text.."\nMissions: "..capmission+interceptmission
|
||||
text = text.."\nAirWing managed Missions: "..capmission+awacsmission+tankermission+reconmission
|
||||
text = text.."\n - CAP: "..capmission
|
||||
text = text.."\n - Intercept: "..interceptmission
|
||||
text = text.."\n - AWACS: "..awacsmission
|
||||
text = text.."\n - TANKER: "..tankermission
|
||||
text = text.."\n - Recon: "..reconmission
|
||||
text = text.."\nSelf managed Missions:"
|
||||
text = text.."\n - Mission Limit: "..self.MaxAliveMissions
|
||||
text = text.."\n - Alert5+Intercept "..self:_CountAliveAuftrags()
|
||||
MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug)
|
||||
end
|
||||
self:__Status(30)
|
||||
|
||||
@@ -445,6 +445,21 @@ function LEGION:DelCohort(Cohort)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove specific asset from legion.
|
||||
-- @param #LEGION self
|
||||
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset.
|
||||
-- @return #LEGION self
|
||||
function LEGION:DelAsset(Asset)
|
||||
|
||||
if Asset.cohort then
|
||||
Asset.cohort:DelAsset(Asset)
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: Asset has not cohort attached. Cannot remove it from legion!"))
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Relocate a cohort to another legion.
|
||||
-- Assets in stock are spawned and routed to the new legion.
|
||||
@@ -1643,6 +1658,9 @@ function LEGION:onafterAssetDead(From, Event, To, asset, request)
|
||||
if self.commander and self.commander.chief then
|
||||
self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname})
|
||||
end
|
||||
|
||||
-- Remove asset from cohort and legion.
|
||||
self:DelAsset(asset)
|
||||
|
||||
-- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function
|
||||
-- Remove asset from squadron same
|
||||
|
||||
@@ -512,7 +512,7 @@ OPSGROUP.CargoStatus={
|
||||
|
||||
--- OpsGroup version.
|
||||
-- @field #string version
|
||||
OPSGROUP.version="1.0.3"
|
||||
OPSGROUP.version="1.0.4"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1333,8 +1333,9 @@ end
|
||||
-- @param Core.Point#COORDINATE TargetCoord Coordinate of the target.
|
||||
-- @param #number WeaponBitType Weapon type.
|
||||
-- @param Core.Point#COORDINATE RefCoord Reference coordinate.
|
||||
-- @param #table SurfaceTypes Valid surfaces types of the coordinate. Default any (nil).
|
||||
-- @return Core.Point#COORDINATE Coordinate in weapon range
|
||||
function OPSGROUP:GetCoordinateInRange(TargetCoord, WeaponBitType, RefCoord)
|
||||
function OPSGROUP:GetCoordinateInRange(TargetCoord, WeaponBitType, RefCoord, SurfaceTypes)
|
||||
|
||||
local coordInRange=nil --Core.Point#COORDINATE
|
||||
|
||||
@@ -1343,35 +1344,58 @@ function OPSGROUP:GetCoordinateInRange(TargetCoord, WeaponBitType, RefCoord)
|
||||
-- Get weapon range.
|
||||
local weapondata=self:GetWeaponData(WeaponBitType)
|
||||
|
||||
-- Heading intervals to search for a possible new coordinate in range.
|
||||
local dh={0, -5, 5, -10, 10, -15, 15, -20, 20, -25, 25, -30, 30, -35, 35, -40, 40, -45, 45, -50, 50, -55, 55, -60, 60, -65, 65, -70, 70, -75, 75, -80, 80}
|
||||
|
||||
-- Function that checks if the given surface type is valid
|
||||
local function _checkSurface(point)
|
||||
if SurfaceTypes then
|
||||
local stype=point:GetSurfaceType()
|
||||
for _,sf in pairs(SurfaceTypes) do
|
||||
if sf==stype then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if weapondata then
|
||||
|
||||
-- Heading to target.
|
||||
local heading=RefCoord:HeadingTo(TargetCoord)
|
||||
local heading=TargetCoord:HeadingTo(RefCoord)
|
||||
|
||||
-- Distance to target.
|
||||
local dist=RefCoord:Get2DDistance(TargetCoord)
|
||||
|
||||
local range=nil
|
||||
if dist>weapondata.RangeMax then
|
||||
range=weapondata.RangeMax
|
||||
self:T(self.lid..string.format("Out of max range = %.1f km by %.1f km for weapon %s", weapondata.RangeMax/1000, (weapondata.RangeMax-dist)/1000, tostring(WeaponBitType)))
|
||||
elseif dist<weapondata.RangeMin then
|
||||
range=weapondata.RangeMin
|
||||
self:T(self.lid..string.format("Out of min range = %.1f km by %.1f km for weapon %s", weapondata.RangeMin/1000, (weapondata.RangeMin-dist)/1000, tostring(WeaponBitType)))
|
||||
end
|
||||
|
||||
-- Check if we are within range.
|
||||
if dist>weapondata.RangeMax then
|
||||
|
||||
local d=(dist-weapondata.RangeMax)*1.05
|
||||
|
||||
-- New waypoint coord.
|
||||
coordInRange=RefCoord:Translate(d, heading)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Out of max range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(WeaponBitType)))
|
||||
elseif dist<weapondata.RangeMin then
|
||||
|
||||
local d=(dist-weapondata.RangeMin)*1.05
|
||||
|
||||
-- New waypoint coord.
|
||||
coordInRange=RefCoord:Translate(d, heading)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Out of min range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(WeaponBitType)))
|
||||
else
|
||||
if range then
|
||||
|
||||
for _,delta in pairs(dh) do
|
||||
|
||||
local h=heading+delta
|
||||
|
||||
-- New waypoint coord.
|
||||
coordInRange=TargetCoord:Translate(range, h)
|
||||
|
||||
if _checkSurface(coordInRange) then
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Already in range for weapon %s", tostring(WeaponBitType)))
|
||||
end
|
||||
@@ -1450,11 +1474,14 @@ end
|
||||
-- @param #number RangeMin Minimum range in nautical miles. Default 0 NM.
|
||||
-- @param #number RangeMax Maximum range in nautical miles. Default 10 NM.
|
||||
-- @param #number BitType Bit mask of weapon type for which the given min/max ranges apply. Default is `ENUMS.WeaponFlag.Auto`, i.e. for all weapon types.
|
||||
-- @param #function ConversionToMeters Function that converts input units of ranges to meters. Defaul `UTILS.NMToMeters`.
|
||||
-- @return #OPSGROUP self
|
||||
function OPSGROUP:AddWeaponRange(RangeMin, RangeMax, BitType)
|
||||
function OPSGROUP:AddWeaponRange(RangeMin, RangeMax, BitType, ConversionToMeters)
|
||||
|
||||
RangeMin=UTILS.NMToMeters(RangeMin or 0)
|
||||
RangeMax=UTILS.NMToMeters(RangeMax or 10)
|
||||
ConversionToMeters=ConversionToMeters or UTILS.NMToMeters
|
||||
|
||||
RangeMin=ConversionToMeters(RangeMin or 0)
|
||||
RangeMax=ConversionToMeters(RangeMax or 10)
|
||||
|
||||
local weapon={} --#OPSGROUP.WeaponData
|
||||
|
||||
@@ -6073,17 +6100,16 @@ function OPSGROUP:RouteToMission(mission, delay)
|
||||
-- Target Coord.
|
||||
local targetcoord=mission:GetTargetCoordinate()
|
||||
|
||||
|
||||
-- In range already?
|
||||
local inRange=self:InWeaponRange(targetcoord, mission.engageWeaponType)
|
||||
local inRange=self:InWeaponRange(targetcoord, mission.engageWeaponType, waypointcoord)
|
||||
|
||||
if inRange then
|
||||
|
||||
waypointcoord=self:GetCoordinate(true)
|
||||
--waypointcoord=self:GetCoordinate(true)
|
||||
|
||||
else
|
||||
|
||||
local coordInRange=self:GetCoordinateInRange(targetcoord, mission.engageWeaponType, waypointcoord)
|
||||
local coordInRange=self:GetCoordinateInRange(targetcoord, mission.engageWeaponType, waypointcoord, surfacetypes)
|
||||
|
||||
if coordInRange then
|
||||
|
||||
@@ -9019,7 +9045,7 @@ function OPSGROUP:AddWeightCargo(UnitName, Weight)
|
||||
self:T(self.lid..string.format("%s: Adding %.1f kg cargo weight. New cargo weight=%.1f kg", UnitName, Weight, element.weightCargo))
|
||||
|
||||
-- For airborne units, we set the weight in game.
|
||||
if self.isFlightgroup then
|
||||
if self.isFlightgroup and element.unit and element.unit:IsAlive() then -- #2272 trying to deduct cargo weight from possibly dead units
|
||||
trigger.action.setUnitInternalCargo(element.name, element.weightCargo) --https://wiki.hoggitworld.com/view/DCS_func_setUnitInternalCargo
|
||||
end
|
||||
|
||||
|
||||
@@ -723,6 +723,7 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @return #OPSZONE self
|
||||
function OPSZONE:onafterStart(From, Event, To)
|
||||
|
||||
-- Info.
|
||||
@@ -739,6 +740,7 @@ function OPSZONE:onafterStart(From, Event, To)
|
||||
self:HandleEvent(EVENTS.BaseCaptured)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stop OPSZONE FSM.
|
||||
|
||||
@@ -106,7 +106,7 @@ PLAYERRECCE = {
|
||||
ClassName = "PLAYERRECCE",
|
||||
verbose = true,
|
||||
lid = nil,
|
||||
version = "0.1.24",
|
||||
version = "0.1.26",
|
||||
ViewZone = {},
|
||||
ViewZoneVisual = {},
|
||||
ViewZoneLaser = {},
|
||||
@@ -154,7 +154,8 @@ PLAYERRECCE.LaserRelativePos = {
|
||||
["SA342Minigun"] = { x = 1.7, y = 1.2, z = 0 },
|
||||
["SA342L"] = { x = 1.7, y = 1.2, z = 0 },
|
||||
["Ka-50"] = { x = 6.1, y = -0.85 , z = 0 },
|
||||
["Ka-50_3"] = { x = 6.1, y = -0.85 , z = 0 }
|
||||
["Ka-50_3"] = { x = 6.1, y = -0.85 , z = 0 },
|
||||
["OH58D"] = {x = 0, y = 2.8, z = 0},
|
||||
}
|
||||
|
||||
---
|
||||
@@ -166,7 +167,8 @@ PLAYERRECCE.MaxViewDistance = {
|
||||
["SA342Minigun"] = 8000,
|
||||
["SA342L"] = 8000,
|
||||
["Ka-50"] = 8000,
|
||||
["Ka-50_3"] = 8000,
|
||||
["Ka-50_3"] = 8000,
|
||||
["OH58D"] = 8000,
|
||||
}
|
||||
|
||||
---
|
||||
@@ -178,7 +180,8 @@ PLAYERRECCE.Cameraheight = {
|
||||
["SA342Minigun"] = 2.85,
|
||||
["SA342L"] = 2.85,
|
||||
["Ka-50"] = 0.5,
|
||||
["Ka-50_3"] = 0.5,
|
||||
["Ka-50_3"] = 0.5,
|
||||
["OH58D"] = 4.25,
|
||||
}
|
||||
|
||||
---
|
||||
@@ -190,7 +193,8 @@ PLAYERRECCE.CanLase = {
|
||||
["SA342Minigun"] = false, -- no optics
|
||||
["SA342L"] = true,
|
||||
["Ka-50"] = true,
|
||||
["Ka-50_3"] = true,
|
||||
["Ka-50_3"] = true,
|
||||
["OH58D"] = false, -- has onboard and useable laser
|
||||
}
|
||||
|
||||
---
|
||||
@@ -546,7 +550,7 @@ function PLAYERRECCE:SetAttackSet(AttackSet)
|
||||
return self
|
||||
end
|
||||
|
||||
---[Internal] Check Gazelle camera in on
|
||||
---[Internal] Check Helicopter camera in on
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param Wrapper.Client#CLIENT client
|
||||
-- @param #string playername
|
||||
@@ -562,6 +566,12 @@ function PLAYERRECCE:_CameraOn(client,playername)
|
||||
if vivihorizontal < -0.7 or vivihorizontal > 0.7 then
|
||||
camera = false
|
||||
end
|
||||
elseif string.find(typename,"OH58") then
|
||||
local dcsunit = Unit.getByName(client:GetName())
|
||||
local vivihorizontal = dcsunit:getDrawArgumentValue(528) or 0 -- Kiow
|
||||
if vivihorizontal < -0.527 or vivihorizontal > 0.527 then
|
||||
camera = false
|
||||
end
|
||||
elseif string.find(typename,"Ka-50") then
|
||||
camera = true
|
||||
end
|
||||
@@ -569,6 +579,52 @@ function PLAYERRECCE:_CameraOn(client,playername)
|
||||
return camera
|
||||
end
|
||||
|
||||
--- [Internal] Get the view parameters from a Kiowa MMS camera
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param Wrapper.Unit#UNIT Kiowa
|
||||
-- @return #number cameraheading in degrees.
|
||||
-- @return #number cameranodding in degrees.
|
||||
-- @return #number maxview in meters.
|
||||
-- @return #boolean cameraison If true, camera is on, else off.
|
||||
function PLAYERRECCE:_GetKiowaMMSSight(Kiowa)
|
||||
self:T(self.lid.."_GetKiowaMMSSight")
|
||||
local unit = Kiowa -- Wrapper.Unit#UNIT
|
||||
if unit and unit:IsAlive() then
|
||||
local dcsunit = Unit.getByName(Kiowa:GetName())
|
||||
--[[
|
||||
shagrat — 01/01/2025 23:13
|
||||
Found the necessary ARGS for the Kiowa MMS angle and rotation:
|
||||
Arg 527 vertical movement
|
||||
0 = neutral
|
||||
-1.0 = max depression (30° max depression angle)
|
||||
+1.0 = max elevation angle (30° max elevation angle)
|
||||
|
||||
Arg 528 horizontal movement
|
||||
0 = forward (0 degr)
|
||||
-0.25 = 90° left
|
||||
-0.5 = rear (180°) left (max 190° = -0.527
|
||||
+0.25 = 90° right
|
||||
+0.5 = 180° right (max 190° = 0.527)
|
||||
--]]
|
||||
local mmshorizontal = dcsunit:getDrawArgumentValue(528) or 0
|
||||
local mmsvertical = dcsunit:getDrawArgumentValue(527) or 0
|
||||
self:T(string.format("Kiowa MMS Arguments Read: H %.3f V %.3f",mmshorizontal,mmsvertical))
|
||||
local mmson = true
|
||||
if mmshorizontal < -0.527 or mmshorizontal > 0.527 then mmson = false end
|
||||
local horizontalview = mmshorizontal / 0.527 * 190
|
||||
local heading = unit:GetHeading()
|
||||
local mmsheading = (heading+horizontalview)%360
|
||||
--local mmsyaw = mmsvertical * 30
|
||||
local mmsyaw = math.atan(mmsvertical)*40
|
||||
local maxview = self:_GetActualMaxLOSight(unit,mmsheading, mmsyaw,not mmson)
|
||||
if maxview > 8000 then maxview = 8000 end
|
||||
self:T(string.format("Kiowa MMS Heading %d, Yaw %d, MaxView %dm MMS On %s",mmsheading,mmsyaw,maxview,tostring(mmson)))
|
||||
return mmsheading,mmsyaw,maxview,mmson
|
||||
end
|
||||
return 0,0,0,false
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Get the view parameters from a Gazelle camera
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param Wrapper.Unit#UNIT Gazelle
|
||||
@@ -597,40 +653,15 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle)
|
||||
vivioff = true
|
||||
return 0,0,0,false
|
||||
end
|
||||
vivivertical = vivivertical / 1.10731 -- normalize
|
||||
|
||||
local horizontalview = vivihorizontal * -180
|
||||
local verticalview = vivivertical * 30 -- ca +/- 30°
|
||||
--self:I(string.format("vivihorizontal=%.5f | vivivertical=%.5f",vivihorizontal,vivivertical))
|
||||
--self:I(string.format("horizontal=%.5f | vertical=%.5f",horizontalview,verticalview))
|
||||
--local verticalview = vivivertical * 30 -- ca +/- 30°
|
||||
local verticalview = math.atan(vivivertical)
|
||||
|
||||
local heading = unit:GetHeading()
|
||||
local viviheading = (heading+horizontalview)%360
|
||||
local maxview = self:_GetActualMaxLOSight(unit,viviheading, verticalview,vivioff)
|
||||
--self:I(string.format("maxview=%.5f",maxview))
|
||||
-- visual skew
|
||||
local factor = 3.15
|
||||
self.GazelleViewFactors = {
|
||||
[1]=1.18,
|
||||
[2]=1.32,
|
||||
[3]=1.46,
|
||||
[4]=1.62,
|
||||
[5]=1.77,
|
||||
[6]=1.85,
|
||||
[7]=2.05,
|
||||
[8]=2.05,
|
||||
[9]=2.3,
|
||||
[10]=2.3,
|
||||
[11]=2.27,
|
||||
[12]=2.27,
|
||||
[13]=2.43,
|
||||
}
|
||||
local lfac = UTILS.Round(maxview,-2)
|
||||
if lfac <= 1300 then
|
||||
--factor = self.GazelleViewFactors[lfac/100]
|
||||
factor = 3.15
|
||||
maxview = math.ceil((maxview*factor)/100)*100
|
||||
end
|
||||
if maxview > 8000 then maxview = 8000 end
|
||||
--self:I(string.format("corrected maxview=%.5f",maxview))
|
||||
return viviheading, verticalview,maxview, not vivioff
|
||||
end
|
||||
return 0,0,0,false
|
||||
@@ -651,20 +682,20 @@ function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff)
|
||||
if unit and unit:IsAlive() then
|
||||
local typename = unit:GetTypeName()
|
||||
maxview = self.MaxViewDistance[typename] or 8000
|
||||
local CamHeight = self.Cameraheight[typename] or 0
|
||||
if vnod < 0 then
|
||||
local CamHeight = self.Cameraheight[typename] or 1
|
||||
if vnod < -2 then
|
||||
-- Looking down
|
||||
-- determine max distance we're looking at
|
||||
local beta = 90
|
||||
local gamma = math.floor(90-vnod)
|
||||
local alpha = math.floor(180-beta-gamma)
|
||||
local gamma = 90-math.abs(vnod)
|
||||
local alpha = 90-gamma
|
||||
local a = unit:GetHeight()-unit:GetCoordinate():GetLandHeight()+CamHeight
|
||||
local b = a / math.sin(math.rad(alpha))
|
||||
local c = b * math.sin(math.rad(gamma))
|
||||
maxview = c*1.2 -- +20%
|
||||
end
|
||||
end
|
||||
return math.abs(maxview)
|
||||
return math.ceil(math.abs(maxview))
|
||||
end
|
||||
|
||||
--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns.
|
||||
@@ -673,8 +704,10 @@ end
|
||||
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers.
|
||||
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||
-- callsigns from playername or group name.
|
||||
-- @param #func CallsignCustomFunc (Optional) For player names only(!). If given, this function will return the callsign. Needs to take the groupname and the playername as first two arguments.
|
||||
-- @param #arg ... (Optional) Comma separated arguments to add to the custom function call after groupname and playername.
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations,CallsignCustomFunc,...)
|
||||
if not ShortCallsign or ShortCallsign == false then
|
||||
self.ShortCallsign = false
|
||||
else
|
||||
@@ -682,6 +715,8 @@ function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTransla
|
||||
end
|
||||
self.Keepnumber = Keepnumber or false
|
||||
self.CallsignTranslations = CallsignTranslations
|
||||
self.CallsignCustomFunc = CallsignCustomFunc
|
||||
self.CallsignCustomArgs = arg or {}
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -803,7 +838,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera,laser)
|
||||
local minview = 0
|
||||
local typename = unit:GetTypeName()
|
||||
local playername = unit:GetPlayerName()
|
||||
local maxview = self.MaxViewDistance[typename] or 5000
|
||||
local maxview = self.MaxViewDistance[typename] or 8000
|
||||
local heading,nod,maxview,angle = 0,30,8000,10
|
||||
local camon = false
|
||||
local name = unit:GetName()
|
||||
@@ -811,16 +846,25 @@ function PLAYERRECCE:_GetTargetSet(unit,camera,laser)
|
||||
heading,nod,maxview,camon = self:_GetGazelleVivianneSight(unit)
|
||||
angle=10
|
||||
-- Model nod and actual TV view don't compute
|
||||
maxview = self.MaxViewDistance[typename] or 5000
|
||||
maxview = self.MaxViewDistance[typename] or 8000
|
||||
elseif string.find(typename,"Ka-50") and camera then
|
||||
heading = unit:GetHeading()
|
||||
nod,maxview,camon = 10,1000,true
|
||||
angle = 10
|
||||
maxview = self.MaxViewDistance[typename] or 5000
|
||||
maxview = self.MaxViewDistance[typename] or 8000
|
||||
elseif string.find(typename,"OH58") and camera then
|
||||
--heading = unit:GetHeading()
|
||||
nod,maxview,camon = 0,8000,true
|
||||
heading,nod,maxview,camon = self:_GetKiowaMMSSight(unit)
|
||||
angle = 8
|
||||
if maxview == 0 then
|
||||
maxview = self.MaxViewDistance[typename] or 8000
|
||||
end
|
||||
else
|
||||
-- visual
|
||||
heading = unit:GetHeading()
|
||||
nod,maxview,camon = 10,1000,true
|
||||
nod,maxview,camon = 10,3000,true
|
||||
maxview = self.MaxViewDistance[typename] or 3000
|
||||
angle = 45
|
||||
end
|
||||
if laser then
|
||||
@@ -1361,6 +1405,7 @@ function PLAYERRECCE:_BuildMenus(Client)
|
||||
local client = _client -- Wrapper.Client#CLIENT
|
||||
if client and client:IsAlive() then
|
||||
local playername = client:GetPlayerName()
|
||||
self:T("Menu for "..playername)
|
||||
if not self.UnitLaserCodes[playername] then
|
||||
self:_SetClientLaserCode(nil,nil,playername,1688)
|
||||
end
|
||||
@@ -1369,6 +1414,7 @@ function PLAYERRECCE:_BuildMenus(Client)
|
||||
end
|
||||
local group = client:GetGroup()
|
||||
if not self.ClientMenus[playername] then
|
||||
self:T("Start Menubuild for "..playername)
|
||||
local canlase = self.CanLase[client:GetTypeName()]
|
||||
self.ClientMenus[playername] = MENU_GROUP:New(group,self.MenuName or self.Name or "RECCE")
|
||||
local txtonstation = self.OnStation[playername] and "ON" or "OFF"
|
||||
@@ -1506,8 +1552,9 @@ end
|
||||
-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS.
|
||||
-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest)
|
||||
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS
|
||||
-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
|
||||
function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Backend)
|
||||
self:T(self.lid.."SetSRS")
|
||||
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" --
|
||||
self.Gender = Gender or MSRS.gender or "male" --
|
||||
@@ -1529,6 +1576,9 @@ function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,V
|
||||
self.SRS:SetCulture(self.Culture)
|
||||
self.SRS:SetPort(self.Port)
|
||||
self.SRS:SetVolume(self.Volume)
|
||||
if Backend then
|
||||
self.SRS:SetBackend(Backend)
|
||||
end
|
||||
if self.PathToGoogleKey then
|
||||
self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.PathToGoogleKey)
|
||||
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
@@ -1585,6 +1635,15 @@ function PLAYERRECCE:EnableSmokeOwnPosition()
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Enable auto lasing for the Kiowa OH-58D.
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:EnableKiowaAutolase()
|
||||
self:T(self.lid.."EnableKiowaAutolase")
|
||||
self.CanLase.OH58D = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Disable smoking of own position
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @return #PLAYERRECCE
|
||||
@@ -1742,7 +1801,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition)
|
||||
if self.ReferencePoint then
|
||||
@@ -1751,7 +1810,7 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername)
|
||||
end
|
||||
local text1 = "Party time!"
|
||||
local text2 = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext)
|
||||
local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext)
|
||||
local text2tts = string.format(" All stations, FACA %s on station at %s!",callsign, coordtext)
|
||||
text2tts = self:_GetTextForSpeech(text2tts)
|
||||
if self.debug then
|
||||
self:T(text2.."\n"..text2tts)
|
||||
@@ -1782,7 +1841,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition)
|
||||
if self.ReferencePoint then
|
||||
@@ -1922,7 +1981,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterIllumination(From, Event, To, Client, Playername, TargetSet)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition)
|
||||
if self.AttackSet then
|
||||
@@ -1965,7 +2024,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterTargetsSmoked(From, Event, To, Client, Playername, TargetSet)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition)
|
||||
if self.AttackSet then
|
||||
@@ -2008,7 +2067,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterTargetsFlared(From, Event, To, Client, Playername, TargetSet)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition)
|
||||
if self.AttackSet then
|
||||
@@ -2052,7 +2111,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Lasercode, Lasingtime)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition,Settings)
|
||||
@@ -2099,7 +2158,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterShack(From, Event, To, Client, Target)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition,Settings)
|
||||
@@ -2146,7 +2205,7 @@ end
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS
|
||||
local coord = Client:GetCoordinate()
|
||||
local coordtext = coord:ToStringBULLS(self.Coalition,Settings)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
-- ===
|
||||
-- @module Ops.PlayerTask
|
||||
-- @image OPS_PlayerTask.jpg
|
||||
-- @date Last Update Nov 2024
|
||||
-- @date Last Update Jan 2025
|
||||
|
||||
|
||||
do
|
||||
@@ -98,7 +98,7 @@ PLAYERTASK = {
|
||||
|
||||
--- PLAYERTASK class version.
|
||||
-- @field #string version
|
||||
PLAYERTASK.version="0.1.24"
|
||||
PLAYERTASK.version="0.1.25"
|
||||
|
||||
--- Generic task condition.
|
||||
-- @type PLAYERTASK.Condition
|
||||
@@ -700,6 +700,15 @@ function PLAYERTASK:IsDone()
|
||||
return IsDone
|
||||
end
|
||||
|
||||
--- [User] Check if task is NOT done
|
||||
-- @param #PLAYERTASK self
|
||||
-- @return #boolean done
|
||||
function PLAYERTASK:IsNotDone()
|
||||
self:T(self.lid.."IsNotDone?")
|
||||
local IsNotDone = not self:IsDone()
|
||||
return IsNotDone
|
||||
end
|
||||
|
||||
--- [User] Check if PLAYERTASK has clients assigned to it.
|
||||
-- @param #PLAYERTASK self
|
||||
-- @return #boolean hasclients
|
||||
@@ -1156,7 +1165,7 @@ function PLAYERTASK:onafterCancel(From, Event, To)
|
||||
self.TaskController:__TaskCancelled(-1,self)
|
||||
end
|
||||
self.timestamp = timer.getAbsTime()
|
||||
self.FinalState = "Cancel"
|
||||
self.FinalState = "Cancelled"
|
||||
self:__Done(-1)
|
||||
return self
|
||||
end
|
||||
@@ -1175,7 +1184,7 @@ function PLAYERTASK:onafterSuccess(From, Event, To)
|
||||
if self.TargetMarker then
|
||||
self.TargetMarker:Remove()
|
||||
end
|
||||
if self.TaskController.Scoring then
|
||||
if self.TaskController and self.TaskController.Scoring then
|
||||
local clients,count = self:GetClientObjects()
|
||||
if count > 0 then
|
||||
for _,_client in pairs(clients) do
|
||||
@@ -1233,7 +1242,7 @@ end
|
||||
do
|
||||
-------------------------------------------------------------------------------------------------------------------
|
||||
-- PLAYERTASKCONTROLLER
|
||||
-- TODO: PLAYERTASKCONTROLLER
|
||||
-- TODO: PLAYERTASKCONTROLLER
|
||||
-- DONE Playername customized
|
||||
-- DONE Coalition-level screen info to SET based
|
||||
-- DONE Flash directions
|
||||
@@ -1308,6 +1317,8 @@ do
|
||||
-- @field Core.ClientMenu#CLIENTMENU ActiveTopMenu
|
||||
-- @field Core.ClientMenu#CLIENTMENU ActiveInfoMenu
|
||||
-- @field Core.ClientMenu#CLIENTMENU MenuNoTask
|
||||
-- @field #boolean InformationMenu Show Radio Info Menu
|
||||
-- @field #number TaskInfoDuration How long to show the briefing info on the screen
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
---
|
||||
@@ -1663,6 +1674,8 @@ PLAYERTASKCONTROLLER = {
|
||||
UseTypeNames = false,
|
||||
Scoring = nil,
|
||||
MenuNoTask = nil,
|
||||
InformationMenu = false,
|
||||
TaskInfoDuration = 30,
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1799,6 +1812,7 @@ PLAYERTASKCONTROLLER.Messages = {
|
||||
CRUISER = "Cruiser",
|
||||
DESTROYER = "Destroyer",
|
||||
CARRIER = "Aircraft Carrier",
|
||||
RADIOS = "Radios",
|
||||
},
|
||||
DE = {
|
||||
TASKABORT = "Auftrag abgebrochen!",
|
||||
@@ -1882,12 +1896,13 @@ PLAYERTASKCONTROLLER.Messages = {
|
||||
CRUISER = "Kreuzer",
|
||||
DESTROYER = "Zerstörer",
|
||||
CARRIER = "Flugzeugträger",
|
||||
RADIOS = "Frequenzen",
|
||||
},
|
||||
}
|
||||
|
||||
--- PLAYERTASK class version.
|
||||
-- @field #string version
|
||||
PLAYERTASKCONTROLLER.version="0.1.67"
|
||||
PLAYERTASKCONTROLLER.version="0.1.69"
|
||||
|
||||
--- Create and run a new TASKCONTROLLER instance.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
@@ -1920,6 +1935,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
||||
self.TaskQueue = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.PrecisionTasks = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.LasingDroneSet = SET_OPSGROUP:New() -- Core.Set#SET_OPSGROUP
|
||||
--self.PlayerMenu = {} -- #table
|
||||
self.FlashPlayer = {} -- #table
|
||||
self.AllowFlash = false
|
||||
@@ -1949,6 +1965,10 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
||||
|
||||
self.UseTypeNames = false
|
||||
|
||||
self.InformationMenu = false
|
||||
|
||||
self.TaskInfoDuration = 30
|
||||
|
||||
self.IsClientSet = false
|
||||
|
||||
if ClientFilter and type(ClientFilter) == "table" and ClientFilter.ClassName and ClientFilter.ClassName == "SET_CLIENT" then
|
||||
@@ -2166,6 +2186,16 @@ function PLAYERTASKCONTROLLER:SetAllowFlashDirection(OnOff)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set to show a menu entry to retrieve the radio frequencies used.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #boolean OnOff Set to `true` to switch on and `false` to switch off. Default is OFF.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetShowRadioInfoMenu(OnOff)
|
||||
self:T(self.lid.."SetAllowRadioInfoMenu")
|
||||
self.InformationMenu = OnOff
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Do not show menu entries to smoke or flare targets
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
@@ -2232,8 +2262,10 @@ end
|
||||
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers.
|
||||
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||
-- callsigns from playername or group name.
|
||||
-- @param #func CallsignCustomFunc (Optional) For player names only(!). If given, this function will return the callsign. Needs to take the groupname and the playername as first two arguments.
|
||||
-- @param #arg ... (Optional) Comma separated arguments to add to the custom function call after groupname and playername.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations,CallsignCustomFunc,...)
|
||||
if not ShortCallsign or ShortCallsign == false then
|
||||
self.ShortCallsign = false
|
||||
else
|
||||
@@ -2241,6 +2273,8 @@ function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber,Callsi
|
||||
end
|
||||
self.Keepnumber = Keepnumber or false
|
||||
self.CallsignTranslations = CallsignTranslations
|
||||
self.CallsignCustomFunc = CallsignCustomFunc
|
||||
self.CallsignCustomArgs = arg or {}
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2261,7 +2295,7 @@ function PLAYERTASKCONTROLLER:_GetTextForSpeech(text)
|
||||
return text
|
||||
end
|
||||
|
||||
--- [User] Set repetition options for tasks
|
||||
--- [User] Set repetition options for tasks.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #boolean OnOff Set to `true` to switch on and `false` to switch off (defaults to true)
|
||||
-- @param #number Repeats Number of repeats (defaults to 5)
|
||||
@@ -2279,6 +2313,16 @@ function PLAYERTASKCONTROLLER:SetTaskRepetition(OnOff, Repeats)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set how long the briefing is shown on screen.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #number Seconds Duration in seconds. Defaults to 30 seconds.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetBriefingDuration(Seconds)
|
||||
self:T(self.lid.."SetBriefingDuration")
|
||||
self.TaskInfoDuration = Seconds or 30
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Send message to SET_CLIENT of players
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #string Text the text to be send
|
||||
@@ -2305,6 +2349,7 @@ end
|
||||
-- @param Core.Point#COORDINATE HoldingPoint (Optional) Point where the drone should initially circle. If not set, defaults to BullsEye of the coalition.
|
||||
-- @param #number Alt (Optional) Altitude in feet. Only applies if using a FLIGHTGROUP object! Defaults to 10000.
|
||||
-- @param #number Speed (Optional) Speed in knots. Only applies if using a FLIGHTGROUP object! Defaults to 120.
|
||||
-- @param #number MaxTravelDist (Optional) Max distance to travel to traget. Only applies if using a FLIGHTGROUP object! Defaults to 100 NM.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
-- @usage
|
||||
-- -- Set up precision bombing, FlightGroup as lasing unit
|
||||
@@ -2319,35 +2364,56 @@ end
|
||||
-- ArmyGroup:Activate()
|
||||
-- taskmanager:EnablePrecisionBombing(ArmyGroup,1688)
|
||||
--
|
||||
function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,HoldingPoint, Alt, Speed)
|
||||
function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,HoldingPoint,Alt,Speed,MaxTravelDist)
|
||||
self:T(self.lid.."EnablePrecisionBombing")
|
||||
|
||||
if not self.LasingDroneSet then
|
||||
self.LasingDroneSet = SET_OPSGROUP:New()
|
||||
end
|
||||
|
||||
local LasingDrone -- Ops.FlightGroup#FLIGHTGROUP FlightGroup
|
||||
|
||||
if FlightGroup then
|
||||
if FlightGroup.ClassName and (FlightGroup.ClassName == "FLIGHTGROUP" or FlightGroup.ClassName == "ARMYGROUP")then
|
||||
-- ok we have a FG
|
||||
self.LasingDrone = FlightGroup -- Ops.FlightGroup#FLIGHTGROUP FlightGroup
|
||||
self.LasingDrone.playertask = {}
|
||||
self.LasingDrone.playertask.busy = false
|
||||
self.LasingDrone.playertask.id = 0
|
||||
LasingDrone = FlightGroup -- Ops.FlightGroup#FLIGHTGROUP FlightGroup
|
||||
|
||||
self.precisionbombing = true
|
||||
self.LasingDrone:SetLaser(LaserCode)
|
||||
self.LaserCode = LaserCode or 1688
|
||||
self.LasingDroneTemplate = self.LasingDrone:_GetTemplate(true)
|
||||
self.LasingDroneAlt = Alt or 10000
|
||||
self.LasingDroneSpeed = Speed or 120
|
||||
|
||||
LasingDrone.playertask = {}
|
||||
LasingDrone.playertask.id = 0
|
||||
LasingDrone.playertask.busy = false
|
||||
LasingDrone.playertask.lasercode = LaserCode or 1688
|
||||
LasingDrone:SetLaser(LasingDrone.playertask.lasercode)
|
||||
LasingDrone.playertask.template = LasingDrone:_GetTemplate(true)
|
||||
LasingDrone.playertask.alt = Alt or 10000
|
||||
LasingDrone.playertask.speed = Speed or 120
|
||||
LasingDrone.playertask.maxtravel = UTILS.NMToMeters(MaxTravelDist or 50)
|
||||
|
||||
-- let it orbit the BullsEye if FG
|
||||
if self.LasingDrone:IsFlightgroup() then
|
||||
self.LasingDroneIsFlightgroup = true
|
||||
if LasingDrone:IsFlightgroup() then
|
||||
--settings.IsFlightgroup = true
|
||||
local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( self.Coalition ))
|
||||
if HoldingPoint then BullsCoordinate = HoldingPoint end
|
||||
local Orbit = AUFTRAG:NewORBIT_CIRCLE(BullsCoordinate,self.LasingDroneAlt,self.LasingDroneSpeed)
|
||||
self.LasingDrone:AddMission(Orbit)
|
||||
elseif self.LasingDrone:IsArmygroup() then
|
||||
self.LasingDroneIsArmygroup = true
|
||||
local Orbit = AUFTRAG:NewORBIT_CIRCLE(BullsCoordinate,Alt,Speed)
|
||||
Orbit:SetMissionAltitude(Alt)
|
||||
LasingDrone:AddMission(Orbit)
|
||||
elseif LasingDrone:IsArmygroup() then
|
||||
--settings.IsArmygroup = true
|
||||
local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( self.Coalition ))
|
||||
if HoldingPoint then BullsCoordinate = HoldingPoint end
|
||||
local Orbit = AUFTRAG:NewONGUARD(BullsCoordinate)
|
||||
self.LasingDrone:AddMission(Orbit)
|
||||
LasingDrone:AddMission(Orbit)
|
||||
end
|
||||
|
||||
self.LasingDroneSet:AddObject(FlightGroup)
|
||||
|
||||
elseif FlightGroup.ClassName and (FlightGroup.ClassName == "SET_OPSGROUP") then --SET_OPSGROUP
|
||||
FlightGroup:ForEachGroup(
|
||||
function(group)
|
||||
self:EnablePrecisionBombing(group,LaserCode,HoldingPoint,Alt,Speed,MaxTravelDist)
|
||||
end
|
||||
)
|
||||
else
|
||||
self:E(self.lid.."No FLIGHTGROUP object passed or FLIGHTGROUP is not alive!")
|
||||
end
|
||||
@@ -2358,6 +2424,20 @@ function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,Holdi
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Convenience function - add done or ground allowing precision laser-guided bombing on statics and "high-value" ground units (MBT etc)
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FlightGroup (e.g. drone) to be used for lasing (one unit in one group only).
|
||||
-- Can optionally be handed as Ops.ArmyGroup#ARMYGROUP - **Note** might not find an LOS spot or get lost on the way. Cannot island-hop.
|
||||
-- @param #number LaserCode The lasercode to be used. Defaults to 1688.
|
||||
-- @param Core.Point#COORDINATE HoldingPoint (Optional) Point where the drone should initially circle. If not set, defaults to BullsEye of the coalition.
|
||||
-- @param #number Alt (Optional) Altitude in feet. Only applies if using a FLIGHTGROUP object! Defaults to 10000.
|
||||
-- @param #number Speed (Optional) Speed in knots. Only applies if using a FLIGHTGROUP object! Defaults to 120.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:AddPrecisionBombingOpsGroup(FlightGroup,LaserCode,HoldingPoint, Alt, Speed)
|
||||
self:EnablePrecisionBombing(FlightGroup,LaserCode,HoldingPoint,Alt,Speed)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [User] Allow precision laser-guided bombing on statics and "high-value" ground units (MBT etc) with player units lasing.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
@@ -2442,7 +2522,7 @@ function PLAYERTASKCONTROLLER:_GetPlayerName(Client)
|
||||
if not self.customcallsigns[playername] then
|
||||
local playergroup = Client:GetGroup()
|
||||
if playergroup ~= nil then
|
||||
ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local newplayername = self:_GetTextForSpeech(ttsplayername)
|
||||
self.customcallsigns[playername] = newplayername
|
||||
ttsplayername = newplayername
|
||||
@@ -2582,7 +2662,7 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
||||
if self.customcallsigns[playername] then
|
||||
self.customcallsigns[playername] = nil
|
||||
end
|
||||
playername = EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber)
|
||||
playername = EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
end
|
||||
playername = self:_GetTextForSpeech(playername)
|
||||
--local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext)
|
||||
@@ -2879,99 +2959,155 @@ end
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:_CheckPrecisionTasks()
|
||||
self:T(self.lid.."_CheckPrecisionTasks")
|
||||
self:T({count=self.PrecisionTasks:Count(),enabled=self.precisionbombing})
|
||||
if self.PrecisionTasks:Count() > 0 and self.precisionbombing then
|
||||
if not self.LasingDrone or self.LasingDrone:IsDead() then
|
||||
-- we need a new drone
|
||||
self:E(self.lid.."Lasing drone is dead ... creating a new one!")
|
||||
if self.LasingDrone then
|
||||
self.LasingDrone:_Respawn(1,nil,true)
|
||||
else
|
||||
-- DONE: Handle ArmyGroup
|
||||
if self.LasingDroneIsFlightgroup then
|
||||
local FG = FLIGHTGROUP:New(self.LasingDroneTemplate)
|
||||
FG:Activate()
|
||||
self:EnablePrecisionBombing(FG,self.LaserCode or 1688)
|
||||
|
||||
-- alive checks
|
||||
self.LasingDroneSet:ForEachGroup(
|
||||
function(LasingDrone)
|
||||
if not LasingDrone or LasingDrone:IsDead() then
|
||||
-- we need a new drone
|
||||
self:E(self.lid.."Lasing drone is dead ... creating a new one!")
|
||||
if LasingDrone then
|
||||
LasingDrone:_Respawn(1,nil,true)
|
||||
else
|
||||
local FG = ARMYGROUP:New(self.LasingDroneTemplate)
|
||||
FG:Activate()
|
||||
self:EnablePrecisionBombing(FG,self.LaserCode or 1688)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
-- do we have a lasing unit assigned?
|
||||
if self.LasingDrone and self.LasingDrone:IsAlive() then
|
||||
if self.LasingDrone.playertask and (not self.LasingDrone.playertask.busy) then
|
||||
-- not busy, get a task
|
||||
self:T(self.lid.."Sending lasing unit to target")
|
||||
local task = self.PrecisionTasks:Pull() -- Ops.PlayerTask#PLAYERTASK
|
||||
self.LasingDrone.playertask.id = task.PlayerTaskNr
|
||||
self.LasingDrone.playertask.busy = true
|
||||
self.LasingDrone.playertask.inreach = false
|
||||
self.LasingDrone.playertask.reachmessage = false
|
||||
-- move the drone to target
|
||||
if self.LasingDroneIsFlightgroup then
|
||||
self.LasingDrone:CancelAllMissions()
|
||||
local auftrag = AUFTRAG:NewORBIT_CIRCLE(task.Target:GetCoordinate(),self.LasingDroneAlt,self.LasingDroneSpeed)
|
||||
self.LasingDrone:AddMission(auftrag)
|
||||
elseif self.LasingDroneIsArmygroup then
|
||||
local tgtcoord = task.Target:GetCoordinate()
|
||||
local tgtzone = ZONE_RADIUS:New("ArmyGroup-"..math.random(1,10000),tgtcoord:GetVec2(),3000)
|
||||
local finalpos=nil -- Core.Point#COORDINATE
|
||||
for i=1,50 do
|
||||
finalpos = tgtzone:GetRandomCoordinate(2500,0,{land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.SHALLOW_WATER})
|
||||
if finalpos then
|
||||
if finalpos:IsLOS(tgtcoord,0) then
|
||||
break
|
||||
end
|
||||
--[[
|
||||
-- DONE: Handle ArmyGroup
|
||||
if LasingDrone:IsFlightgroup() then
|
||||
local FG = FLIGHTGROUP:New(LasingDroneTemplate)
|
||||
FG:Activate()
|
||||
self:EnablePrecisionBombing(FG,self.LaserCode or 1688)
|
||||
else
|
||||
local FG = ARMYGROUP:New(LasingDroneTemplate)
|
||||
FG:Activate()
|
||||
self:EnablePrecisionBombing(FG,self.LaserCode or 1688)
|
||||
end -- if LasingDroneIsFlightgroup
|
||||
--]]
|
||||
end -- if LasingDrone
|
||||
end -- if not LasingDrone
|
||||
end -- function
|
||||
)
|
||||
|
||||
local function SelectDrone(coord)
|
||||
local selected = nil
|
||||
local mindist = math.huge
|
||||
local dist = math.huge
|
||||
self.LasingDroneSet:ForEachGroup(
|
||||
function(grp)
|
||||
if grp.playertask and (not grp.playertask.busy) then
|
||||
local gc = grp:GetCoordinate()
|
||||
if coord and gc then
|
||||
dist = coord:Get2DDistance(gc)
|
||||
end
|
||||
if dist < mindist then
|
||||
selected = grp
|
||||
mindist = dist
|
||||
end
|
||||
end
|
||||
if finalpos then
|
||||
self.LasingDrone:CancelAllMissions()
|
||||
-- yeah we got one
|
||||
local auftrag = AUFTRAG:NewARMOREDGUARD(finalpos,"Off road")
|
||||
self.LasingDrone:AddMission(auftrag)
|
||||
else
|
||||
-- could not find LOS position!
|
||||
self:E("***Could not find LOS position to post ArmyGroup for lasing!")
|
||||
self.LasingDrone.playertask.id = 0
|
||||
self.LasingDrone.playertask.busy = false
|
||||
self.LasingDrone.playertask.inreach = false
|
||||
self.LasingDrone.playertask.reachmessage = false
|
||||
end
|
||||
end
|
||||
self.PrecisionTasks:Push(task,task.PlayerTaskNr)
|
||||
elseif self.LasingDrone.playertask and self.LasingDrone.playertask.busy then
|
||||
)
|
||||
return selected
|
||||
end
|
||||
|
||||
local task = self.PrecisionTasks:Pull() -- Ops.PlayerTask#PLAYERTASK
|
||||
local taskpt = task.Target:GetCoordinate()
|
||||
|
||||
local SelectedDrone = SelectDrone(taskpt) -- Ops.OpsGroup#OPSGROUP
|
||||
|
||||
-- do we have a lasing unit assignable?
|
||||
if SelectedDrone and SelectedDrone:IsAlive() then
|
||||
if SelectedDrone.playertask and (not SelectedDrone.playertask.busy) then
|
||||
-- not busy, get a task
|
||||
self:T(self.lid.."Sending lasing unit to target")
|
||||
local isassigned = self:_FindLasingDroneForTaskID(task.PlayerTaskNr)
|
||||
-- distance check
|
||||
local startpoint = SelectedDrone:GetCoordinate()
|
||||
local endpoint = task.Target:GetCoordinate()
|
||||
local dist = math.huge
|
||||
if startpoint and endpoint then
|
||||
dist = startpoint:Get2DDistance(endpoint)
|
||||
end
|
||||
if dist <= SelectedDrone.playertask.maxtravel and (not isassigned) then
|
||||
SelectedDrone.playertask.id = task.PlayerTaskNr
|
||||
SelectedDrone.playertask.busy = true
|
||||
SelectedDrone.playertask.inreach = false
|
||||
SelectedDrone.playertask.reachmessage = false
|
||||
-- move the drone to target
|
||||
if SelectedDrone:IsFlightgroup() then
|
||||
SelectedDrone:CancelAllMissions()
|
||||
local auftrag = AUFTRAG:NewORBIT_CIRCLE(task.Target:GetCoordinate(),SelectedDrone.playertask.alt,SelectedDrone.playertask.speed)
|
||||
SelectedDrone:AddMission(auftrag)
|
||||
elseif SelectedDrone:IsArmygroup() then
|
||||
local tgtcoord = task.Target:GetCoordinate()
|
||||
local tgtzone = ZONE_RADIUS:New("ArmyGroup-"..math.random(1,10000),tgtcoord:GetVec2(),3000)
|
||||
local finalpos=nil -- Core.Point#COORDINATE
|
||||
for i=1,50 do
|
||||
finalpos = tgtzone:GetRandomCoordinate(2500,0,{land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.SHALLOW_WATER})
|
||||
if finalpos then
|
||||
if finalpos:IsLOS(tgtcoord,0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if finalpos then
|
||||
SelectedDrone:CancelAllMissions()
|
||||
-- yeah we got one
|
||||
local auftrag = AUFTRAG:NewARMOREDGUARD(finalpos,"Off road")
|
||||
SelectedDrone:AddMission(auftrag)
|
||||
else
|
||||
-- could not find LOS position!
|
||||
self:E("***Could not find LOS position to post ArmyGroup for lasing!")
|
||||
SelectedDrone.playertask.id = 0
|
||||
SelectedDrone.playertask.busy = false
|
||||
SelectedDrone.playertask.inreach = false
|
||||
SelectedDrone.playertask.reachmessage = false
|
||||
end
|
||||
end
|
||||
else
|
||||
self:T(self.lid.."Lasing unit too far from target")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
self.PrecisionTasks:Push(task,task.PlayerTaskNr)
|
||||
|
||||
|
||||
local function DronesWithTask(SelectedDrone)
|
||||
-- handle drones with a task
|
||||
if SelectedDrone.playertask and SelectedDrone.playertask.busy then
|
||||
-- drone is busy, set up laser when over target
|
||||
local task = self.PrecisionTasks:ReadByID(self.LasingDrone.playertask.id) -- Ops.PlayerTask#PLAYERTASK
|
||||
local task = self.PrecisionTasks:ReadByID(SelectedDrone.playertask.id) -- Ops.PlayerTask#PLAYERTASK
|
||||
self:T("Looking at Task: "..task.PlayerTaskNr.." Type: "..task.Type.." State: "..task:GetState())
|
||||
if (not task) or task:GetState() == "Done" or task:GetState() == "Stopped" then
|
||||
-- we're done here
|
||||
local task = self.PrecisionTasks:PullByID(self.LasingDrone.playertask.id) -- Ops.PlayerTask#PLAYERTASK
|
||||
local task = self.PrecisionTasks:PullByID(SelectedDrone.playertask.id) -- Ops.PlayerTask#PLAYERTASK
|
||||
self:_CheckTaskQueue()
|
||||
task = nil
|
||||
if self.LasingDrone:IsLasing() then
|
||||
self.LasingDrone:__LaserOff(-1)
|
||||
if SelectedDrone:IsLasing() then
|
||||
SelectedDrone:__LaserOff(-1)
|
||||
end
|
||||
self.LasingDrone.playertask.busy = false
|
||||
self.LasingDrone.playertask.inreach = false
|
||||
self.LasingDrone.playertask.id = 0
|
||||
self.LasingDrone.playertask.reachmessage = false
|
||||
SelectedDrone.playertask.busy = false
|
||||
SelectedDrone.playertask.inreach = false
|
||||
SelectedDrone.playertask.id = 0
|
||||
SelectedDrone.playertask.reachmessage = false
|
||||
self:T(self.lid.."Laser Off")
|
||||
else
|
||||
-- not done yet
|
||||
local dcoord = self.LasingDrone:GetCoordinate()
|
||||
self:T(self.lid.."Not done yet")
|
||||
local dcoord = SelectedDrone:GetCoordinate()
|
||||
local tcoord = task.Target:GetCoordinate()
|
||||
tcoord.y = tcoord.y + 2
|
||||
local dist = dcoord:Get2DDistance(tcoord)
|
||||
self:T(self.lid.."Dist "..dist)
|
||||
-- close enough?
|
||||
if dist < 3000 and not self.LasingDrone:IsLasing() then
|
||||
if dist < 3000 and not SelectedDrone:IsLasing() then
|
||||
self:T(self.lid.."Laser On")
|
||||
self.LasingDrone:__LaserOn(-1,tcoord)
|
||||
self.LasingDrone.playertask.inreach = true
|
||||
if not self.LasingDrone.playertask.reachmessage then
|
||||
SelectedDrone:__LaserOn(-1,tcoord)
|
||||
SelectedDrone.playertask.inreach = true
|
||||
if not SelectedDrone.playertask.reachmessage then
|
||||
--local textmark = self.gettext:GetEntry("FLARETASK",self.locale)
|
||||
self.LasingDrone.playertask.reachmessage = true
|
||||
SelectedDrone.playertask.reachmessage = true
|
||||
local clients = task:GetClients()
|
||||
local text = ""
|
||||
for _,playername in pairs(clients) do
|
||||
@@ -2979,7 +3115,7 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks()
|
||||
local ttsplayername = playername
|
||||
if self.customcallsigns[playername] then
|
||||
ttsplayername = self.customcallsigns[playername]
|
||||
end
|
||||
end --
|
||||
--text = string.format("%s, %s, pointer over target for task %03d, lasing!", playername, self.MenuName or self.Name, task.PlayerTaskNr)
|
||||
text = string.format(pointertext, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr)
|
||||
if not self.NoScreenOutput then
|
||||
@@ -2991,18 +3127,21 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks()
|
||||
)
|
||||
if client then
|
||||
local m = MESSAGE:New(text,15,"Tasking"):ToClient(client)
|
||||
end
|
||||
end
|
||||
end
|
||||
end --
|
||||
end --
|
||||
end --
|
||||
if self.UseSRS then
|
||||
self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end --
|
||||
end --
|
||||
end --
|
||||
end -- end else
|
||||
end -- end handle drones with a task
|
||||
end -- end function
|
||||
|
||||
self.LasingDroneSet:ForEachGroup(DronesWithTask)
|
||||
|
||||
end --
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -3464,6 +3603,32 @@ function PLAYERTASKCONTROLLER:_SwitchFlashing(Group, Client)
|
||||
return self
|
||||
end
|
||||
|
||||
function PLAYERTASKCONTROLLER:_ShowRadioInfo(Group, Client)
|
||||
self:T(self.lid.."_ShowRadioInfo")
|
||||
local playername, ttsplayername = self:_GetPlayerName(Client)
|
||||
|
||||
if self.UseSRS then
|
||||
local frequency = self.Frequency
|
||||
local freqtext = ""
|
||||
if type(frequency) == "table" then
|
||||
freqtext = self.gettext:GetEntry("FREQUENCIES",self.locale)
|
||||
freqtext = freqtext..table.concat(frequency,", ")
|
||||
else
|
||||
local freqt = self.gettext:GetEntry("FREQUENCY",self.locale)
|
||||
freqtext = string.format(freqt,frequency)
|
||||
end
|
||||
|
||||
local switchtext = self.gettext:GetEntry("BROADCAST",self.locale)
|
||||
|
||||
playername = ttsplayername or self:_GetTextForSpeech(playername)
|
||||
--local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext)
|
||||
local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext)
|
||||
self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2,{Group},text,30,self.BCFrequency,self.BCModulation)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Flashing directional info for a client
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
@@ -3489,6 +3654,22 @@ function PLAYERTASKCONTROLLER:_FlashInfo()
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Find matching drone for precision bombing task, if any is assigned.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param #number ID Task ID to look for
|
||||
-- @return Ops.OpsGroup#OPSGROUP Drone
|
||||
function PLAYERTASKCONTROLLER:_FindLasingDroneForTaskID(ID)
|
||||
local drone = nil
|
||||
self.LasingDroneSet:ForEachGroup(
|
||||
function(grp)
|
||||
if grp and grp:IsAlive() and grp.playertask and grp.playertask.id and grp.playertask.id == ID then
|
||||
drone = grp
|
||||
end
|
||||
end
|
||||
)
|
||||
return drone
|
||||
end
|
||||
|
||||
--- [Internal] Show active task info
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
-- @param Ops.PlayerTask#PLAYERTASK Task
|
||||
@@ -3516,6 +3697,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
||||
local Elevation = Coordinate:GetLandHeight() or 0 -- meters
|
||||
local CoordText = ""
|
||||
local CoordTextLLDM = nil
|
||||
local LasingDrone = self:_FindLasingDroneForTaskID(task.PlayerTaskNr)
|
||||
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
|
||||
CoordText = Coordinate:ToStringA2G(Client,nil,self.ShowMagnetic)
|
||||
else
|
||||
@@ -3551,14 +3733,14 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
||||
text = text .. string.format(elev,tostring(math.floor(Elevation)),elevationmeasure)
|
||||
-- Prec bombing
|
||||
if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then
|
||||
if self.LasingDrone and self.LasingDrone.playertask then
|
||||
if LasingDrone and LasingDrone.playertask then
|
||||
local yes = self.gettext:GetEntry("YES",self.locale)
|
||||
local no = self.gettext:GetEntry("NO",self.locale)
|
||||
local inreach = self.LasingDrone.playertask.inreach == true and yes or no
|
||||
local islasing = self.LasingDrone:IsLasing() == true and yes or no
|
||||
local inreach = LasingDrone.playertask.inreach == true and yes or no
|
||||
local islasing = LasingDrone:IsLasing() == true and yes or no
|
||||
local prectext = self.gettext:GetEntry("POINTERTARGETREPORT",self.locale)
|
||||
prectext = string.format(prectext,inreach,islasing)
|
||||
text = text .. prectext.." ("..self.LaserCode..")"
|
||||
text = text .. prectext.." ("..LasingDrone.playertask.lasercode..")"
|
||||
end
|
||||
end
|
||||
-- Buddylasing
|
||||
@@ -3576,7 +3758,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
||||
local pcoord = player:GetCoordinate()
|
||||
if pcoord:Get2DDistance(Coordinate) <= reachdist then
|
||||
inreach = true
|
||||
local callsign = player:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local callsign = player:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
|
||||
local playername = player:GetPlayerName()
|
||||
local islasing = no
|
||||
if self.PlayerRecce.CanLase[player:GetTypeName()] and self.PlayerRecce.AutoLase[playername] then
|
||||
@@ -3663,7 +3845,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
||||
local ttstext = string.format(ThreatLocaleTextTTS,ttsplayername,self.MenuName or self.Name,ttstaskname,ThreatLevelText, targets, CoordText)
|
||||
-- POINTERTARGETLASINGTTS = ". Pointer over target and lasing."
|
||||
if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then
|
||||
if self.LasingDrone.playertask.inreach and self.LasingDrone:IsLasing() then
|
||||
if LasingDrone and LasingDrone.playertask.inreach and LasingDrone:IsLasing() then
|
||||
local lasingtext = self.gettext:GetEntry("POINTERTARGETLASINGTTS",self.locale)
|
||||
ttstext = ttstext .. lasingtext
|
||||
end
|
||||
@@ -3683,7 +3865,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
||||
text = self.gettext:GetEntry("NOACTIVETASK",self.locale)
|
||||
end
|
||||
if not self.NoScreenOutput then
|
||||
local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
|
||||
local m=MESSAGE:New(text,self.TaskInfoDuration or 30,"Tasking"):ToClient(Client)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -4037,6 +4219,11 @@ function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate()
|
||||
self.MenuNoTask = nil
|
||||
end
|
||||
|
||||
if self.InformationMenu then
|
||||
local radioinfo = self.gettext:GetEntry("RADIOS",self.locale)
|
||||
JoinTaskMenuTemplate:NewEntry(radioinfo,self.JoinTopMenu,self._ShowRadioInfo,self)
|
||||
end
|
||||
|
||||
self.JoinTaskMenuTemplate = JoinTaskMenuTemplate
|
||||
|
||||
return self
|
||||
@@ -4376,8 +4563,9 @@ end
|
||||
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here.
|
||||
-- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate from which the controller radio is sending
|
||||
-- @param #string Backend (Optional) MSRS Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC; if you use a config file for MSRS, hand in nil here.
|
||||
-- @return #PLAYERTASKCONTROLLER self
|
||||
function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate)
|
||||
function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate,Backend)
|
||||
self:T(self.lid.."SetSRS")
|
||||
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" --
|
||||
self.Gender = Gender or MSRS.gender or "male" --
|
||||
@@ -4393,7 +4581,7 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu
|
||||
self.Modulation = Modulation or {radio.modulation.FM,radio.modulation.AM} --
|
||||
self.BCModulation = self.Modulation
|
||||
-- set up SRS
|
||||
self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation)
|
||||
self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,Backend)
|
||||
self.SRS:SetCoalition(self.Coalition)
|
||||
self.SRS:SetLabel(self.MenuName or self.Name)
|
||||
self.SRS:SetGender(self.Gender)
|
||||
|
||||
@@ -387,6 +387,8 @@ function TARGET:AddObject(Object)
|
||||
|
||||
if Object:IsInstanceOf("OPSGROUP") then
|
||||
self:_AddObject(Object:GetGroup()) -- We add the MOOSE GROUP object not the OPSGROUP object.
|
||||
--elseif Object:IsInstanceOf("OPSZONE") then
|
||||
--self:_AddObject(Object:GetZone())
|
||||
else
|
||||
self:_AddObject(Object)
|
||||
end
|
||||
@@ -1296,11 +1298,27 @@ function TARGET:GetTargetThreatLevelMax(Target)
|
||||
return 0
|
||||
|
||||
elseif Target.Type==TARGET.ObjectType.ZONE then
|
||||
|
||||
local zone = Target.Object -- Core.Zone#ZONE_RADIUS
|
||||
local foundunits = {}
|
||||
if zone:IsInstanceOf("ZONE_RADIUS") or zone:IsInstanceOf("ZONE_POLYGON") then
|
||||
zone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT,Unit.Category.SHIP})
|
||||
foundunits = zone:GetScannedSetUnit()
|
||||
else
|
||||
foundunits = SET_UNIT:New():FilterZones({zone}):FilterOnce()
|
||||
end
|
||||
local ThreatMax = foundunits:GetThreatLevelMax() or 0
|
||||
return ThreatMax
|
||||
|
||||
return 0
|
||||
elseif Target.Type==TARGET.ObjectType.OPSZONE then
|
||||
|
||||
local unitset = Target.Object:GetScannedUnitSet() -- Core.Set#SET_UNIT
|
||||
local ThreatMax = unitset:GetThreatLevelMax()
|
||||
return ThreatMax
|
||||
|
||||
else
|
||||
self:E("ERROR: unknown target object type in GetTargetThreatLevel!")
|
||||
return 0
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@@ -380,7 +380,8 @@ function RADIOQUEUE:Broadcast(transmission)
|
||||
self:T(self.lid..string.format("Broadcasting from aircraft %s", sender:GetName()))
|
||||
|
||||
|
||||
if not self.senderinit then
|
||||
--if not self.senderinit then
|
||||
-- TODO Seems to be a DCS bug - if I explode ANY unit in a group the BC assignment gets lost
|
||||
|
||||
-- Command to set the Frequency for the transmission.
|
||||
local commandFrequency={
|
||||
@@ -394,7 +395,7 @@ function RADIOQUEUE:Broadcast(transmission)
|
||||
sender:SetCommand(commandFrequency)
|
||||
|
||||
self.senderinit=true
|
||||
end
|
||||
--end
|
||||
|
||||
-- Set subtitle only if duration>0 sec.
|
||||
local subtitle=nil
|
||||
|
||||
@@ -267,6 +267,135 @@ MSRS.version="0.3.3"
|
||||
--- Voices
|
||||
-- @type MSRS.Voices
|
||||
MSRS.Voices = {
|
||||
Amazon = {
|
||||
Generative = {
|
||||
en_AU = {
|
||||
Olivia = "Olivia",
|
||||
},
|
||||
en_GB = {
|
||||
Amy = "Amy",
|
||||
},
|
||||
en_US = {
|
||||
Danielle = "Danielle",
|
||||
Joanna = "Joanna",
|
||||
Ruth = "Ruth",
|
||||
Stephen = "Stephen",
|
||||
},
|
||||
fr_FR = {
|
||||
["Léa"] = "Léa",
|
||||
["Rémi"] = "Rémi",
|
||||
},
|
||||
de_DE = {
|
||||
Vicki = "Vicki",
|
||||
Daniel = "Daniel",
|
||||
},
|
||||
it_IT = {
|
||||
Bianca = "Bianca",
|
||||
Adriano = "Adriano",
|
||||
},
|
||||
es_ES = {
|
||||
Lucia = "Lucia",
|
||||
Sergio = "Sergio",
|
||||
},
|
||||
},
|
||||
LongForm = {
|
||||
en_US = {
|
||||
Danielle = "Danielle",
|
||||
Gregory = "Gregory",
|
||||
Ivy = "Ivy",
|
||||
Ruth = "Ruth",
|
||||
Patrick = "Patrick",
|
||||
},
|
||||
es_ES = {
|
||||
Alba = "Alba",
|
||||
["Raúl"] = "Raúl",
|
||||
},
|
||||
},
|
||||
Neural = {
|
||||
en_AU = {
|
||||
Olivia = "Olivia",
|
||||
},
|
||||
en_GB = {
|
||||
Amy = "Amy",
|
||||
Emma = "Emma",
|
||||
Brian = "Brian",
|
||||
Arthur = "Arthur",
|
||||
},
|
||||
en_US = {
|
||||
Danielle = "Danielle",
|
||||
Gregory = "Gregory",
|
||||
Ivy = "Ivy",
|
||||
Joanna = "Joanna",
|
||||
Kendra = "Kendra",
|
||||
Kimberly = "Kimberly",
|
||||
Salli = "Salli",
|
||||
Joey = "Joey",
|
||||
Kevin = "Kevin",
|
||||
Ruth = "Ruth",
|
||||
Stephen = "Stephen",
|
||||
},
|
||||
fr_FR = {
|
||||
["Léa"] = "Léa",
|
||||
["Rémi"] = "Rémi",
|
||||
},
|
||||
de_DE = {
|
||||
Vicki = "Vicki",
|
||||
Daniel = "Daniel",
|
||||
},
|
||||
it_IT = {
|
||||
Bianca = "Bianca",
|
||||
Adriano = "Adriano",
|
||||
},
|
||||
es_ES = {
|
||||
Lucia = "Lucia",
|
||||
Sergio = "Sergio",
|
||||
},
|
||||
},
|
||||
Standard = {
|
||||
en_AU = {
|
||||
Nicole = "Nicole",
|
||||
Russel = "Russel",
|
||||
},
|
||||
en_GB = {
|
||||
Amy = "Amy",
|
||||
Emma = "Emma",
|
||||
Brian = "Brian",
|
||||
},
|
||||
en_IN = {
|
||||
Aditi = "Aditi",
|
||||
Raveena = "Raveena",
|
||||
},
|
||||
en_US = {
|
||||
Ivy = "Ivy",
|
||||
Joanna = "Joanna",
|
||||
Kendra = "Kendra",
|
||||
Kimberly = "Kimberly",
|
||||
Salli = "Salli",
|
||||
Joey = "Joey",
|
||||
Kevin = "Kevin",
|
||||
},
|
||||
fr_FR = {
|
||||
Celine = "Celine",
|
||||
["Léa"] = "Léa",
|
||||
Mathieu = "Mathieu",
|
||||
},
|
||||
de_DE = {
|
||||
Marlene = "Marlene",
|
||||
Vicki = "Vicki",
|
||||
Hans = "Hans",
|
||||
},
|
||||
it_IT = {
|
||||
Carla = "Carla",
|
||||
Bianca = "Bianca",
|
||||
Giorgio = "Giorgio",
|
||||
},
|
||||
es_ES = {
|
||||
Conchita = "Conchita",
|
||||
Lucia = "Lucia",
|
||||
Enrique = "Enrique",
|
||||
},
|
||||
},
|
||||
},
|
||||
Microsoft = { -- working ones if not using gRPC and MS
|
||||
["Hedda"] = "Microsoft Hedda Desktop", -- de-DE
|
||||
["Hazel"] = "Microsoft Hazel Desktop", -- en-GB
|
||||
@@ -974,7 +1103,7 @@ end
|
||||
-- - `MSRS.Provider.WINDOWS`: Microsoft Windows (default)
|
||||
-- - `MSRS.Provider.GOOGLE`: Google Cloud
|
||||
-- - `MSRS.Provider.AZURE`: Microsoft Azure (only with DCS-gRPC backend)
|
||||
-- - `MSRS.Provier.AMAZON`: Amazone Web Service (only with DCS-gRPC backend)
|
||||
-- - `MSRS.Provier.AMAZON`: Amazon Web Service (only with DCS-gRPC backend)
|
||||
--
|
||||
-- Note that all providers except Microsoft Windows need as additonal information the credentials of your account.
|
||||
--
|
||||
@@ -1184,7 +1313,8 @@ function MSRS:PlaySoundFile(Soundfile, Delay)
|
||||
|
||||
-- Append file.
|
||||
command=command..' --file="'..tostring(soundfile)..'"'
|
||||
|
||||
command=string.gsub(command,"--ssml","-h")
|
||||
|
||||
-- Execute command.
|
||||
self:_ExecCommand(command)
|
||||
|
||||
@@ -1442,7 +1572,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
elseif self.provider==MSRS.Provider.WINDOWS then
|
||||
-- Nothing to do.
|
||||
else
|
||||
self:E("ERROR: SRS only supports WINWOWS and GOOGLE as TTS providers! Use DCS-gRPC backend for other providers such as ")
|
||||
self:E("ERROR: SRS only supports WINDOWS and GOOGLE as TTS providers! Use DCS-gRPC backend for other providers such as AWS and Azure.")
|
||||
end
|
||||
|
||||
if not UTILS.FileExists(fullPath) then
|
||||
@@ -1477,7 +1607,7 @@ function MSRS:_ExecCommand(command)
|
||||
if self.UsePowerShell == true then
|
||||
filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".ps1"
|
||||
batContent = command .. "\'"
|
||||
self:I({batContent=batContent})
|
||||
self:T({batContent=batContent})
|
||||
end
|
||||
|
||||
local script=io.open(filename, "w+")
|
||||
@@ -1660,7 +1790,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab
|
||||
ssml=string.format("<voice%s%s>%s</voice>", gender, language, Text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for _,freq in pairs(Frequencies) do
|
||||
self:T("Calling GRPC.tts with the following parameter:")
|
||||
self:T({ssml=ssml, freq=freq, options=options})
|
||||
@@ -1986,7 +2116,7 @@ end
|
||||
-- @param Core.Point#COORDINATE coordinate Coordinate to be used
|
||||
-- @return #MSRSQUEUE.Transmission Radio transmission table.
|
||||
function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation, gender, culture, voice, volume, label,coordinate)
|
||||
|
||||
self:T({Text=text, Dur=duration, start=tstart, int=interval, sub=subgroups, subt=subtitle, sudb=subduration, F=frequency, M=modulation, G=gender, C=culture, V=voice, Vol=volume, L=label})
|
||||
if self.TransmitOnlyWithPlayers then
|
||||
if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then
|
||||
return self
|
||||
@@ -2026,7 +2156,7 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr
|
||||
transmission.volume = volume or msrs.volume
|
||||
transmission.label = label or msrs.Label
|
||||
transmission.coordinate = coordinate or msrs.coordinate
|
||||
|
||||
|
||||
-- Add transmission to queue.
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Create a SOUNDFILE object (mp3 or ogg) to be played via DCS or SRS transmissions
|
||||
-- * Create a SOUNDTEXT object for text-to-speech output vis SRS Simple-Text-To-Speech (STTS)
|
||||
-- * Create a SOUNDTEXT object for text-to-speech output vis SRS Simple-Text-To-Speech (MSRS)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
|
||||
@@ -1166,6 +1166,127 @@ ENUMS.Storage.weapons.bombs.AGM_62 = "weapons.bombs.AGM_62"
|
||||
ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_WHITE = "weapons.containers.{US_M10_SMOKE_TANK_WHITE}"
|
||||
ENUMS.Storage.weapons.missiles.MICA_T = "weapons.missiles.MICA_T"
|
||||
ENUMS.Storage.weapons.containers.HVAR_rocket = "weapons.containers.HVAR_rocket"
|
||||
-- 2025
|
||||
ENUMS.Storage.weapons.containers.LANTIRN = "weapons.containers.LANTIRN"
|
||||
ENUMS.Storage.weapons.missiles.AGM_78B = "weapons.missiles.AGM_78B"
|
||||
ENUMS.Storage.weapons.containers.uh_60l_pilot = "weapons.containers.uh-60l_pilot"
|
||||
ENUMS.Storage.weapons.missiles.AIM_92E = "weapons.missiles.AIM-92E"
|
||||
ENUMS.Storage.weapons.missiles.KD_63B = "weapons.missiles.KD_63B"
|
||||
ENUMS.Storage.weapons.bombs.Type_200A = "weapons.bombs.Type_200A"
|
||||
ENUMS.Storage.weapons.missiles.HB_AIM_7E_2 = "weapons.missiles.HB-AIM-7E-2"
|
||||
ENUMS.Storage.weapons.containers.Spear = "weapons.containers.Spear"
|
||||
ENUMS.Storage.weapons.missiles.LS_6 = "weapons.missiles.LS_6"
|
||||
ENUMS.Storage.weapons.containers.HB_ALE_40_0_120 = "weapons.containers.HB_ALE_40_0_120"
|
||||
ENUMS.Storage.weapons.containers.Fantasm = "weapons.containers.Fantasm"
|
||||
ENUMS.Storage.weapons.nurs.FFAR_Mk61 = "weapons.nurs.FFAR_Mk61"
|
||||
ENUMS.Storage.weapons.bombs.HB_F4E_GBU15V1 = "weapons.bombs.HB_F4E_GBU15V1"
|
||||
ENUMS.Storage.weapons.containers.HB_F14_EXT_AN_APQ_167 = "weapons.containers.HB_F14_EXT_AN_APQ-167"
|
||||
ENUMS.Storage.weapons.nurs.LWL_RP = "weapons.nurs.LWL_RP"
|
||||
ENUMS.Storage.weapons.bombs.AGM_62_I = "weapons.bombs.AGM_62_I"
|
||||
ENUMS.Storage.weapons.containers.ETHER = "weapons.containers.ETHER"
|
||||
ENUMS.Storage.weapons.containers.TANGAZH = "weapons.containers.TANGAZH"
|
||||
ENUMS.Storage.weapons.bombs.LYSBOMB_11086 = "weapons.bombs.LYSBOMB 11086"
|
||||
ENUMS.Storage.weapons.containers.Stub_Wing = "weapons.containers.Stub_Wing"
|
||||
ENUMS.Storage.weapons.missiles.AIM_9E = "weapons.missiles.AIM-9E"
|
||||
ENUMS.Storage.weapons.missiles.C_701T = "weapons.missiles.C_701T"
|
||||
ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP_100"
|
||||
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM-802AKG"
|
||||
ENUMS.Storage.weapons.missiles.CM_400AKG = "weapons.missiles.CM-400AKG"
|
||||
ENUMS.Storage.weapons.missiles.C_802AK = "weapons.missiles.C_802AK"
|
||||
ENUMS.Storage.weapons.missiles.KD_63 = "weapons.missiles.KD_63"
|
||||
ENUMS.Storage.weapons.containers.HB_ORD_Pave_Spike_Fast = "weapons.containers.HB_ORD_Pave_Spike_Fast"
|
||||
ENUMS.Storage.weapons.missiles.SPIKE_ER2 = "weapons.missiles.SPIKE_ER2"
|
||||
ENUMS.Storage.weapons.containers.KINGAL = "weapons.containers.KINGAL"
|
||||
ENUMS.Storage.weapons.containers.LANTIRN_F14_TARGET = "weapons.containers.LANTIRN-F14-TARGET"
|
||||
ENUMS.Storage.weapons.containers.SPS_141 = "weapons.containers.SPS-141"
|
||||
ENUMS.Storage.weapons.bombs.BLU_3B_GROUP = "weapons.bombs.BLU-3B_GROUP"
|
||||
ENUMS.Storage.weapons.containers.HB_ALE_40_30_0 = "weapons.containers.HB_ALE_40_30_0"
|
||||
ENUMS.Storage.weapons.droptanks.HB_HIGH_PERFORMANCE_CENTERLINE_600_GAL = "weapons.droptanks.HB_HIGH_PERFORMANCE_CENTERLINE_600_GAL"
|
||||
ENUMS.Storage.weapons.containers.ALQ_184 = "weapons.containers.ALQ-184"
|
||||
ENUMS.Storage.weapons.missiles.AGM_45B = "weapons.missiles.AGM_45B"
|
||||
ENUMS.Storage.weapons.bombs.BLU_3_GROUP = "weapons.bombs.BLU-3_GROUP"
|
||||
ENUMS.Storage.weapons.missiles.SPIKE_ER = "weapons.missiles.SPIKE_ER"
|
||||
ENUMS.Storage.weapons.nurs.ARAKM70BAPPX = "weapons.nurs.ARAKM70BAPPX"
|
||||
ENUMS.Storage.weapons.bombs.LYSBOMB_11088 = "weapons.bombs.LYSBOMB 11088"
|
||||
ENUMS.Storage.weapons.bombs.LYSBOMB_11087 = "weapons.bombs.LYSBOMB 11087"
|
||||
ENUMS.Storage.weapons.missiles.KD_20 = "weapons.missiles.KD_20"
|
||||
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank = "weapons.droptanks.HB_F-4E_EXT_WingTank"
|
||||
ENUMS.Storage.weapons.missiles.Rb_04 = "weapons.missiles.Rb_04"
|
||||
ENUMS.Storage.weapons.containers.AAQ_33 = "weapons.containers.AAQ-33"
|
||||
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_Center_Fuel_Tank_EMPTY = "weapons.droptanks.HB_F-4E_EXT_Center_Fuel_Tank_EMPTY"
|
||||
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank_R_EMPTY = "weapons.droptanks.HB_F-4E_EXT_WingTank_R_EMPTY"
|
||||
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank_EMPTY = "weapons.droptanks.HB_F-4E_EXT_WingTank_EMPTY"
|
||||
ENUMS.Storage.weapons.containers.uh_60l_copilot = "weapons.containers.uh-60l_copilot"
|
||||
ENUMS.Storage.weapons.droptanks.JAYHAWK_80gal_Fuel_Tankv2 = "weapons.droptanks.JAYHAWK_80gal_Fuel_Tankv2"
|
||||
ENUMS.Storage.weapons.containers.supply_m134 = "weapons.containers.supply_m134"
|
||||
ENUMS.Storage.weapons.containers.Seahawk_Pylon = "weapons.containers.Seahawk_Pylon"
|
||||
ENUMS.Storage.weapons.nurs.LWL_MPP = "weapons.nurs.LWL_MPP"
|
||||
ENUMS.Storage.weapons.nurs.S_5KP = "weapons.nurs.S_5KP"
|
||||
ENUMS.Storage.weapons.missiles.AIM_92J = "weapons.missiles.AIM-92J"
|
||||
ENUMS.Storage.weapons.missiles.HB_AIM_7E = "weapons.missiles.HB-AIM-7E"
|
||||
ENUMS.Storage.weapons.containers.ALQ_131 = "weapons.containers.ALQ-131"
|
||||
ENUMS.Storage.weapons.containers.HB_F14_EXT_TARPS = "weapons.containers.HB_F14_EXT_TARPS"
|
||||
ENUMS.Storage.weapons.containers.MH60_SOAR = "weapons.containers.MH60_SOAR"
|
||||
ENUMS.Storage.weapons.missiles.YJ_83 = "weapons.missiles.YJ-83"
|
||||
ENUMS.Storage.weapons.bombs.GBU_8_B = "weapons.bombs.GBU_8_B"
|
||||
ENUMS.Storage.weapons.containers.HB_F14_EXT_ECA = "weapons.containers.HB_F14_EXT_ECA"
|
||||
ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP-100"
|
||||
ENUMS.Storage.weapons.nurs.M261_MPSM_Rocket = "weapons.nurs.M261_MPSM_Rocket"
|
||||
ENUMS.Storage.weapons.droptanks.SEAHAWK_120_Fuel_Tank = "weapons.droptanks.SEAHAWK_120_Fuel_Tank"
|
||||
ENUMS.Storage.weapons.containers.SHPIL = "weapons.containers.SHPIL"
|
||||
ENUMS.Storage.weapons.bombs.GBU_39 = "weapons.bombs.GBU_39"
|
||||
ENUMS.Storage.weapons.nurs.S_5M = "weapons.nurs.S_5M"
|
||||
ENUMS.Storage.weapons.containers.HB_ALE_40_15_90 = "weapons.containers.HB_ALE_40_15_90"
|
||||
ENUMS.Storage.weapons.missiles.AIM_7E = "weapons.missiles.AIM-7E"
|
||||
ENUMS.Storage.weapons.missiles.AIM_9P3 = "weapons.missiles.AIM-9P3"
|
||||
ENUMS.Storage.weapons.missiles.AGM_12B = "weapons.missiles.AGM_12B"
|
||||
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM_802AKG"
|
||||
ENUMS.Storage.weapons.droptanks.JAYHAWK_120_Fuel_Dual_Tank = "weapons.droptanks.JAYHAWK_120_Fuel_Dual_Tank"
|
||||
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_Center_Fuel_Tank = "weapons.droptanks.HB_F-4E_EXT_Center_Fuel_Tank"
|
||||
ENUMS.Storage.weapons.containers.PAVETACK = "weapons.containers.PAVETACK"
|
||||
ENUMS.Storage.weapons.missiles.LS_6_500 = "weapons.missiles.LS_6_500"
|
||||
ENUMS.Storage.weapons.bombs.LYSBOMB_11089 = "weapons.bombs.LYSBOMB 11089"
|
||||
ENUMS.Storage.weapons.bombs.BLU_4B_GROUP = "weapons.bombs.BLU-4B_GROUP"
|
||||
ENUMS.Storage.weapons.containers.ah_64d_radar = "weapons.containers.ah-64d_radar"
|
||||
ENUMS.Storage.weapons.containers.F_18_LDT_POD = "weapons.containers.F-18-LDT-POD"
|
||||
ENUMS.Storage.weapons.containers.HB_ALE_40_30_60 = "weapons.containers.HB_ALE_40_30_60"
|
||||
ENUMS.Storage.weapons.bombs.LS_6_100 = "weapons.bombs.LS_6_100"
|
||||
ENUMS.Storage.weapons.droptanks.HB_F_4E_EXT_WingTank_R = "weapons.droptanks.HB_F-4E_EXT_WingTank_R"
|
||||
ENUMS.Storage.weapons.containers.SORBCIJA_R = "weapons.containers.SORBCIJA_R"
|
||||
ENUMS.Storage.weapons.missiles.CATM_65K = "weapons.missiles.CATM_65K"
|
||||
ENUMS.Storage.weapons.containers.HB_ORD_Pave_Spike = "weapons.containers.HB_ORD_Pave_Spike"
|
||||
ENUMS.Storage.weapons.containers.RobbieTank1 = "weapons.containers.RobbieTank1"
|
||||
ENUMS.Storage.weapons.containers.SKY_SHADOW = "weapons.containers.SKY_SHADOW"
|
||||
ENUMS.Storage.weapons.containers.SORBCIJA_L = "weapons.containers.SORBCIJA_L"
|
||||
ENUMS.Storage.weapons.containers.Pavehawk = "weapons.containers.Pavehawk"
|
||||
ENUMS.Storage.weapons.bombs.BLG66_EG = "weapons.bombs.BLG66_EG"
|
||||
ENUMS.Storage.weapons.missiles.AGM_12C_ED = "weapons.missiles.AGM_12C_ED"
|
||||
ENUMS.Storage.weapons.missiles.AIM_92C = "weapons.missiles.AIM-92C"
|
||||
ENUMS.Storage.weapons.containers.MPS_410 = "weapons.containers.MPS-410"
|
||||
ENUMS.Storage.weapons.missiles.HJ_12 = "weapons.missiles.HJ-12"
|
||||
ENUMS.Storage.weapons.containers.AAQ_28_LITENING = "weapons.containers.AAQ-28_LITENING"
|
||||
ENUMS.Storage.weapons.containers.F_18_FLIR_POD = "weapons.containers.F-18-FLIR-POD"
|
||||
ENUMS.Storage.weapons.bombs.BLU_3B_GROUP = "weapons.bombs.BLU_3B_GROUP"
|
||||
ENUMS.Storage.weapons.containers.UH60L_Jayhawk = "weapons.containers.UH60L_Jayhawk"
|
||||
ENUMS.Storage.weapons.containers.BOZ_100 = "weapons.containers.BOZ-100"
|
||||
ENUMS.Storage.weapons.missiles.AGM_78A = "weapons.missiles.AGM_78A"
|
||||
ENUMS.Storage.weapons.missiles.LAU_61_APKWS_M282 = "weapons.missiles.LAU_61_APKWS_M282"
|
||||
ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP-100"
|
||||
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM-802AKG"
|
||||
ENUMS.Storage.weapons.bombs.BLU_3B_GROUP = "weapons.bombs.BLU_3B_GROUP"
|
||||
ENUMS.Storage.weapons.bombs.BLU_4B_GROUP = "weapons.bombs.BLU-4B_GROUP"
|
||||
ENUMS.Storage.weapons.nurs.S_5M = "weapons.nurs.S_5M"
|
||||
ENUMS.Storage.weapons.missiles.AGM_12A = "weapons.missiles.AGM_12A"
|
||||
ENUMS.Storage.weapons.droptanks.JAYHAWK_120_Fuel_Tank = "weapons.droptanks.JAYHAWK_120_Fuel_Tank"
|
||||
ENUMS.Storage.weapons.bombs.GBU_15_V_1_B = "weapons.bombs.GBU_15_V_1_B"
|
||||
ENUMS.Storage.weapons.missiles.HYDRA_70_M151_APKWS = {4,4,8,292}
|
||||
ENUMS.Storage.weapons.missiles.HYDRA_70_M282_APKWS = {4,4,8,293}
|
||||
-- dupes with typos
|
||||
ENUMS.Storage.weapons.bombs.BAP100 = "weapons.bombs.BAP_100"
|
||||
ENUMS.Storage.weapons.bombs.BLU3B_GROUP = "weapons.bombs.BLU-3B_GROUP"
|
||||
ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM_802AKG"
|
||||
ENUMS.Storage.weapons.bombs.BLU_4B_GROUP = "weapons.bombs.BLU_4B_GROUP"
|
||||
ENUMS.Storage.weapons.nurs.S5M = "weapons.nurs.S-5M"
|
||||
-- Gazelle
|
||||
ENUMS.Storage.weapons.Gazelle.HMP400_100RDS = {4,15,46,1771}
|
||||
ENUMS.Storage.weapons.Gazelle.HMP400_200RDS = {4,15,46,1770}
|
||||
@@ -1177,16 +1298,16 @@ ENUMS.Storage.weapons.Gazelle.GIAT_M261_HEAP = {4,15,46,1765}
|
||||
ENUMS.Storage.weapons.Gazelle.GIAT_M261_APHE = {4,15,46,1764}
|
||||
ENUMS.Storage.weapons.Gazelle.GAZELLE_IR_DEFLECTOR = {4,15,47,680}
|
||||
ENUMS.Storage.weapons.Gazelle.GAZELLE_FAS_SANDFILTER = {4,15,47,679}
|
||||
-- Chinook
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M60D = {4,15,46,2476}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M60D = {4,15,46,2477}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M60D = {4,15,46,2478}
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M134D = {4,15,46,2482}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M134D = {4,15,46,2483}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M3M = {4,15,46,2484}
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M240H = {4,15,46,2479}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M240H = {4,15,46,2480}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M240H = {4,15,46,2481}
|
||||
-- Chinook (changed)
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M60D = {4,15,46,2489}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M60D = {4,15,46,2488}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M60D = {4,15,46,2490}
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M134D = {4,15,46,2494}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M134D = {4,15,46,2495}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M3M = {4,15,46,2496} --
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M240H = {4,15,46,2492}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M240H = {4,15,46,2491}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M240H = {4,15,46,2493}
|
||||
-- Huey
|
||||
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Right = {4,15,46,161}
|
||||
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left = {4,15,46,160}
|
||||
@@ -1208,7 +1329,7 @@ ENUMS.Storage.weapons.OH58.Smk_Grenade_Violet = {4,5,9,490}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_White = {4,5,9,492}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_Yellow = {4,5,9,491}
|
||||
-- Apache
|
||||
ENUMS.Storage.weapons.AH64D.AN_APG78 = {4,15,44,2138}
|
||||
ENUMS.Storage.weapons.AH64D.AN_APG78 = {4,15,44,2114}
|
||||
ENUMS.Storage.weapons.AH64D.Internal_Aux_FuelTank = {1,3,43,1700}
|
||||
|
||||
---
|
||||
@@ -1237,3 +1358,4 @@ ENUMS.FARPObjectTypeNamesAndShape ={
|
||||
[ENUMS.FARPType.HELIPADSINGLE] = { TypeName="SINGLE_HELIPAD", ShapeName="FARP"},
|
||||
[ENUMS.FARPType.PADSINGLE] = { TypeName="FARP_SINGLE_01", ShapeName="FARP_SINGLE_01"},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
--- **Utilities** - DCS Simple Text-To-Speech (STTS).
|
||||
--
|
||||
--
|
||||
-- @module Utilities.STTS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
--- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world)
|
||||
-- @type STTS
|
||||
-- @field #string DIRECTORY Path of the SRS directory.
|
||||
|
||||
--- Simple Text-To-Speech
|
||||
--
|
||||
-- Version 0.4 - Compatible with SRS version 1.9.6.0+
|
||||
--
|
||||
-- # DCS Modification Required
|
||||
--
|
||||
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitization.
|
||||
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
|
||||
-- Do this without DCS running to allow mission scripts to use os functions.
|
||||
--
|
||||
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
|
||||
--
|
||||
-- # USAGE:
|
||||
--
|
||||
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialize it
|
||||
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
|
||||
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
|
||||
--
|
||||
-- Example calls:
|
||||
--
|
||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
|
||||
--
|
||||
-- Arguments in order are:
|
||||
--
|
||||
-- * Message to say, make sure not to use a newline (\n) !
|
||||
-- * Frequency in MHz
|
||||
-- * Modulation - AM/FM
|
||||
-- * Volume - 1.0 max, 0.5 half
|
||||
-- * Name of the transmitter - ATC, RockFM etc
|
||||
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||
-- * OPTIONAL - Vec3 Point i.e Unit.getByName("A UNIT"):getPoint() - needs Vec3 for Height! OR null if not needed
|
||||
-- * OPTIONAL - Speed -10 to +10
|
||||
-- * OPTIONAL - Gender male, female or neuter
|
||||
-- * OPTIONAL - Culture - en-US, en-GB etc
|
||||
-- * OPTIONAL - Voice - a specific voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
|
||||
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
|
||||
--
|
||||
--
|
||||
-- ## Example
|
||||
--
|
||||
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
|
||||
--
|
||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
|
||||
--
|
||||
-- ## Example
|
||||
--
|
||||
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
|
||||
--
|
||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
|
||||
--
|
||||
-- Arguments in order are:
|
||||
--
|
||||
-- * FULL path to the MP3 OR OGG to play
|
||||
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
|
||||
-- * Modulation - AM/FM - to use multiple
|
||||
-- * Volume - 1.0 max, 0.5 half
|
||||
-- * Name of the transmitter - ATC, RockFM etc
|
||||
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||
--
|
||||
-- ## Example
|
||||
--
|
||||
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
|
||||
--
|
||||
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
|
||||
--
|
||||
-- @field #STTS
|
||||
STTS = {
|
||||
ClassName = "STTS",
|
||||
DIRECTORY = "",
|
||||
SRS_PORT = 5002,
|
||||
GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json",
|
||||
EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||
}
|
||||
|
||||
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
|
||||
STTS.DIRECTORY = "D:/DCS/_SRS"
|
||||
|
||||
--- LOCAL SRS PORT - DEFAULT IS 5002
|
||||
STTS.SRS_PORT = 5002
|
||||
|
||||
--- Google credentials file
|
||||
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
|
||||
|
||||
--- DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
|
||||
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||
|
||||
--- Function for UUID.
|
||||
function STTS.uuid()
|
||||
local random = math.random
|
||||
local template = 'yxxx-xxxxxxxxxxxx'
|
||||
return string.gsub( template, '[xy]', function( c )
|
||||
local v = (c == 'x') and random( 0, 0xf ) or random( 8, 0xb )
|
||||
return string.format( '%x', v )
|
||||
end )
|
||||
end
|
||||
|
||||
--- Round a number.
|
||||
-- @param #number x Number.
|
||||
-- @param #number n Precision.
|
||||
function STTS.round( x, n )
|
||||
n = math.pow( 10, n or 0 )
|
||||
x = x * n
|
||||
if x >= 0 then
|
||||
x = math.floor( x + 0.5 )
|
||||
else
|
||||
x = math.ceil( x - 0.5 )
|
||||
end
|
||||
return x / n
|
||||
end
|
||||
|
||||
--- Function returns estimated speech time in seconds.
|
||||
-- Assumptions for time calc: 100 Words per min, average of 5 letters for english word so
|
||||
--
|
||||
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
|
||||
--
|
||||
-- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
|
||||
--
|
||||
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||
--
|
||||
-- @param #number length can also be passed as #string
|
||||
-- @param #number speed Defaults to 1.0
|
||||
-- @param #boolean isGoogle We're using Google TTS
|
||||
function STTS.getSpeechTime(length,speed,isGoogle)
|
||||
|
||||
local maxRateRatio = 3
|
||||
|
||||
speed = speed or 1.0
|
||||
isGoogle = isGoogle or false
|
||||
|
||||
local speedFactor = 1.0
|
||||
if isGoogle then
|
||||
speedFactor = speed
|
||||
else
|
||||
if speed ~= 0 then
|
||||
speedFactor = math.abs( speed ) * (maxRateRatio - 1) / 10 + 1
|
||||
end
|
||||
if speed < 0 then
|
||||
speedFactor = 1 / speedFactor
|
||||
end
|
||||
end
|
||||
|
||||
local wpm = math.ceil( 100 * speedFactor )
|
||||
local cps = math.floor( (wpm * 5) / 60 )
|
||||
|
||||
if type( length ) == "string" then
|
||||
length = string.len( length )
|
||||
end
|
||||
|
||||
return length/cps --math.ceil(length/cps)
|
||||
end
|
||||
|
||||
--- Text to speech function.
|
||||
function STTS.TextToSpeech( message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS )
|
||||
if os == nil or io == nil then
|
||||
env.info( "[DCS-STTS] LUA modules os or io are sanitized. skipping. " )
|
||||
return
|
||||
end
|
||||
|
||||
speed = speed or 1
|
||||
gender = gender or "female"
|
||||
culture = culture or ""
|
||||
voice = voice or ""
|
||||
coalition = coalition or "0"
|
||||
name = name or "ROBOT"
|
||||
volume = 1
|
||||
speed = 1
|
||||
|
||||
message = message:gsub( "\"", "\\\"" )
|
||||
|
||||
local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name )
|
||||
|
||||
if voice ~= "" then
|
||||
cmd = cmd .. string.format( " -V \"%s\"", voice )
|
||||
else
|
||||
|
||||
if culture ~= "" then
|
||||
cmd = cmd .. string.format( " -l %s", culture )
|
||||
end
|
||||
|
||||
if gender ~= "" then
|
||||
cmd = cmd .. string.format( " -g %s", gender )
|
||||
end
|
||||
end
|
||||
|
||||
if googleTTS == true then
|
||||
cmd = cmd .. string.format( " -G \"%s\"", STTS.GOOGLE_CREDENTIALS )
|
||||
end
|
||||
|
||||
if speed ~= 1 then
|
||||
cmd = cmd .. string.format( " -s %s", speed )
|
||||
end
|
||||
|
||||
if volume ~= 1.0 then
|
||||
cmd = cmd .. string.format( " -v %s", volume )
|
||||
end
|
||||
|
||||
if point and type( point ) == "table" and point.x then
|
||||
local lat, lon, alt = coord.LOtoLL( point )
|
||||
|
||||
lat = STTS.round( lat, 4 )
|
||||
lon = STTS.round( lon, 4 )
|
||||
alt = math.floor( alt )
|
||||
|
||||
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||
end
|
||||
|
||||
cmd = cmd .. string.format( " -t \"%s\"", message )
|
||||
|
||||
if string.len( cmd ) > 255 then
|
||||
local filename = os.getenv( 'TMP' ) .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
|
||||
local script = io.open( filename, "w+" )
|
||||
script:write( cmd .. " && exit" )
|
||||
script:close()
|
||||
cmd = string.format( "\"%s\"", filename )
|
||||
timer.scheduleFunction( os.remove, filename, timer.getTime() + 1 )
|
||||
end
|
||||
|
||||
if string.len( cmd ) > 255 then
|
||||
env.info( "[DCS-STTS] - cmd string too long" )
|
||||
env.info( "[DCS-STTS] TextToSpeech Command :\n" .. cmd .. "\n" )
|
||||
end
|
||||
os.execute( cmd )
|
||||
|
||||
return STTS.getSpeechTime( message, speed, googleTTS )
|
||||
end
|
||||
|
||||
--- Play mp3 function.
|
||||
-- @param #string pathToMP3 Path to the sound file.
|
||||
-- @param #string freqs Frequencies, e.g. "305, 256".
|
||||
-- @param #string modulations Modulations, e.g. "AM, FM".
|
||||
-- @param #string volume Volume, e.g. "0.5".
|
||||
function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point )
|
||||
|
||||
local cmd = string.format( "start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1" )
|
||||
|
||||
if point and type( point ) == "table" and point.x then
|
||||
local lat, lon, alt = coord.LOtoLL( point )
|
||||
|
||||
lat = STTS.round( lat, 4 )
|
||||
lon = STTS.round( lon, 4 )
|
||||
alt = math.floor( alt )
|
||||
|
||||
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||
end
|
||||
|
||||
env.info( "[DCS-STTS] MP3/OGG Command :\n" .. cmd .. "\n" )
|
||||
os.execute( cmd )
|
||||
|
||||
end
|
||||
@@ -1,612 +0,0 @@
|
||||
--- **Utilities** - Templates.
|
||||
--
|
||||
-- DCS unit templates
|
||||
--
|
||||
-- @module Utilities.Templates
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
--- TEMPLATE class.
|
||||
-- @type TEMPLATE
|
||||
-- @field #string ClassName Name of the class.
|
||||
|
||||
--- *Templates*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Get DCS templates from thin air.
|
||||
--
|
||||
-- # Ground Units
|
||||
--
|
||||
-- Ground units.
|
||||
--
|
||||
-- # Naval Units
|
||||
--
|
||||
-- Ships are not implemented yet.
|
||||
--
|
||||
-- # Aircraft
|
||||
--
|
||||
-- ## Airplanes
|
||||
--
|
||||
-- Airplanes are not implemented yet.
|
||||
--
|
||||
-- ## Helicopters
|
||||
--
|
||||
-- Helicopters are not implemented yet.
|
||||
--
|
||||
-- @field #TEMPLATE
|
||||
TEMPLATE = {
|
||||
ClassName = "TEMPLATE",
|
||||
Ground = {},
|
||||
Naval = {},
|
||||
Airplane = {},
|
||||
Helicopter = {},
|
||||
}
|
||||
|
||||
--- Ground unit type names.
|
||||
-- @type TEMPLATE.TypeGround
|
||||
-- @param #string InfantryAK
|
||||
TEMPLATE.TypeGround={
|
||||
InfantryAK="Infantry AK",
|
||||
ParatrooperAKS74="Paratrooper AKS-74",
|
||||
ParatrooperRPG16="Paratrooper RPG-16",
|
||||
SoldierWWIIUS="soldier_wwii_us",
|
||||
InfantryM248="Infantry M249",
|
||||
SoldierM4="Soldier M4",
|
||||
}
|
||||
|
||||
--- Naval unit type names.
|
||||
-- @type TEMPLATE.TypeNaval
|
||||
-- @param #string Ticonderoga
|
||||
TEMPLATE.TypeNaval={
|
||||
Ticonderoga="TICONDEROG",
|
||||
}
|
||||
|
||||
--- Rotary wing unit type names.
|
||||
-- @type TEMPLATE.TypeAirplane
|
||||
-- @param #string A10C
|
||||
TEMPLATE.TypeAirplane={
|
||||
A10C="A-10C",
|
||||
}
|
||||
|
||||
--- Rotary wing unit type names.
|
||||
-- @type TEMPLATE.TypeHelicopter
|
||||
-- @param #string AH1W
|
||||
TEMPLATE.TypeHelicopter={
|
||||
AH1W="AH-1W",
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Ground Template
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get template for ground units.
|
||||
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||
-- @param #number Nunits Number of units. Default 1.
|
||||
-- @param #number Radius Spawn radius for additonal units in meters. Default 50 m.
|
||||
-- @return #table Template Template table.
|
||||
function TEMPLATE.GetGround(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||
|
||||
-- Defaults.
|
||||
TypeName=TypeName or TEMPLATE.TypeGround.SoldierM4
|
||||
GroupName=GroupName or "Ground-1"
|
||||
CountryID=CountryID or country.id.USA
|
||||
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||
Nunits=Nunits or 1
|
||||
Radius=Radius or 50
|
||||
|
||||
|
||||
-- Get generic template.
|
||||
local template=UTILS.DeepCopy(TEMPLATE.GenericGround)
|
||||
|
||||
-- Set group name.
|
||||
template.name=GroupName
|
||||
|
||||
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||
template.CountryID=CountryID
|
||||
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||
template.CategoryID=Unit.Category.GROUND_UNIT
|
||||
|
||||
-- Set first unit.
|
||||
template.units[1].type=TypeName
|
||||
template.units[1].name=GroupName.."-1"
|
||||
|
||||
if Vec3 then
|
||||
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||
end
|
||||
|
||||
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||
|
||||
return template
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Naval Template
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get template for ground units.
|
||||
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||
-- @param #number Nunits Number of units. Default 1.
|
||||
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||
-- @return #table Template Template table.
|
||||
function TEMPLATE.GetNaval(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||
|
||||
-- Defaults.
|
||||
TypeName=TypeName or TEMPLATE.TypeNaval.Ticonderoga
|
||||
GroupName=GroupName or "Naval-1"
|
||||
CountryID=CountryID or country.id.USA
|
||||
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||
Nunits=Nunits or 1
|
||||
Radius=Radius or 500
|
||||
|
||||
|
||||
-- Get generic template.
|
||||
local template=UTILS.DeepCopy(TEMPLATE.GenericNaval)
|
||||
|
||||
-- Set group name.
|
||||
template.name=GroupName
|
||||
|
||||
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||
template.CountryID=CountryID
|
||||
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||
template.CategoryID=Unit.Category.SHIP
|
||||
|
||||
-- Set first unit.
|
||||
template.units[1].type=TypeName
|
||||
template.units[1].name=GroupName.."-1"
|
||||
|
||||
if Vec3 then
|
||||
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||
end
|
||||
|
||||
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||
|
||||
return template
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Aircraft Template
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get template for fixed wing units.
|
||||
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||
-- @param #number Nunits Number of units. Default 1.
|
||||
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||
-- @return #table Template Template table.
|
||||
function TEMPLATE.GetAirplane(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||
|
||||
-- Defaults.
|
||||
TypeName=TypeName or TEMPLATE.TypeAirplane.A10C
|
||||
GroupName=GroupName or "Airplane-1"
|
||||
CountryID=CountryID or country.id.USA
|
||||
Vec3=Vec3 or {x=0, y=1000, z=0}
|
||||
Nunits=Nunits or 1
|
||||
Radius=Radius or 100
|
||||
|
||||
local template=TEMPLATE._GetAircraft(true, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||
|
||||
return template
|
||||
end
|
||||
|
||||
--- Get template for fixed wing units.
|
||||
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||
-- @param #number Nunits Number of units. Default 1.
|
||||
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||
-- @return #table Template Template table.
|
||||
function TEMPLATE.GetHelicopter(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||
|
||||
-- Defaults.
|
||||
TypeName=TypeName or TEMPLATE.TypeHelicopter.AH1W
|
||||
GroupName=GroupName or "Helicopter-1"
|
||||
CountryID=CountryID or country.id.USA
|
||||
Vec3=Vec3 or {x=0, y=500, z=0}
|
||||
Nunits=Nunits or 1
|
||||
Radius=Radius or 100
|
||||
|
||||
-- Limit unis to 4.
|
||||
Nunits=math.min(Nunits, 4)
|
||||
|
||||
local template=TEMPLATE._GetAircraft(false, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||
|
||||
return template
|
||||
end
|
||||
|
||||
|
||||
--- Get template for aircraft units.
|
||||
-- @param #boolean Airplane If true, this is a fixed wing. Else, rotary wing.
|
||||
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||
-- @param #number Nunits Number of units. Default 1.
|
||||
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||
-- @return #table Template Template table.
|
||||
function TEMPLATE._GetAircraft(Airplane, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||
|
||||
-- Defaults.
|
||||
TypeName=TypeName
|
||||
GroupName=GroupName or "Aircraft-1"
|
||||
CountryID=CountryID or country.id.USA
|
||||
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||
Nunits=Nunits or 1
|
||||
Radius=Radius or 100
|
||||
|
||||
-- Get generic template.
|
||||
local template=UTILS.DeepCopy(TEMPLATE.GenericAircraft)
|
||||
|
||||
-- Set group name.
|
||||
template.name=GroupName
|
||||
|
||||
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||
template.CountryID=CountryID
|
||||
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||
if Airplane then
|
||||
template.CategoryID=Unit.Category.AIRPLANE
|
||||
else
|
||||
template.CategoryID=Unit.Category.HELICOPTER
|
||||
end
|
||||
|
||||
-- Set first unit.
|
||||
template.units[1].type=TypeName
|
||||
template.units[1].name=GroupName.."-1"
|
||||
|
||||
-- Set position.
|
||||
if Vec3 then
|
||||
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||
end
|
||||
|
||||
-- Set number of units.
|
||||
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||
|
||||
return template
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Misc Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set the position of the template.
|
||||
-- @param #table Template The template to be modified.
|
||||
-- @param DCS#Vec2 Vec2 2D Position vector with x and y components of the group.
|
||||
function TEMPLATE.SetPositionFromVec2(Template, Vec2)
|
||||
|
||||
Template.x=Vec2.x
|
||||
Template.y=Vec2.y
|
||||
|
||||
for _,unit in pairs(Template.units) do
|
||||
unit.x=Vec2.x
|
||||
unit.y=Vec2.y
|
||||
end
|
||||
|
||||
Template.route.points[1].x=Vec2.x
|
||||
Template.route.points[1].y=Vec2.y
|
||||
Template.route.points[1].alt=0 --TODO: Use land height.
|
||||
|
||||
end
|
||||
|
||||
--- Set the position of the template.
|
||||
-- @param #table Template The template to be modified.
|
||||
-- @param DCS#Vec3 Vec3 Position vector of the group.
|
||||
function TEMPLATE.SetPositionFromVec3(Template, Vec3)
|
||||
|
||||
local Vec2={x=Vec3.x, y=Vec3.z}
|
||||
|
||||
TEMPLATE.SetPositionFromVec2(Template, Vec2)
|
||||
|
||||
end
|
||||
|
||||
--- Set the position of the template.
|
||||
-- @param #table Template The template to be modified.
|
||||
-- @param #number N Total number of units in the group.
|
||||
-- @param Core.Point#COORDINATE Coordinate Position of the first unit.
|
||||
-- @param #number Radius Radius in meters to randomly place the additional units.
|
||||
function TEMPLATE.SetUnits(Template, N, Coordinate, Radius)
|
||||
|
||||
local units=Template.units
|
||||
|
||||
local unit1=units[1]
|
||||
|
||||
local Vec3=Coordinate:GetVec3()
|
||||
|
||||
unit1.x=Vec3.x
|
||||
unit1.y=Vec3.z
|
||||
unit1.alt=Vec3.y
|
||||
|
||||
for i=2,N do
|
||||
units[i]=UTILS.DeepCopy(unit1)
|
||||
end
|
||||
|
||||
for i=1,N do
|
||||
local unit=units[i]
|
||||
unit.name=string.format("%s-%d", Template.name, i)
|
||||
if i>1 then
|
||||
local vec2=Coordinate:GetRandomCoordinateInRadius(Radius, 5):GetVec2()
|
||||
unit.x=vec2.x
|
||||
unit.y=vec2.y
|
||||
unit.alt=unit1.alt
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Set the position of the template.
|
||||
-- @param #table Template The template to be modified.
|
||||
-- @param Wrapper.Airbase#AIRBASE AirBase The airbase where the aircraft are spawned.
|
||||
-- @param #table ParkingSpots List of parking spot IDs. Every unit needs one!
|
||||
-- @param #boolean EngineOn If true, aircraft are spawned hot.
|
||||
function TEMPLATE.SetAirbase(Template, AirBase, ParkingSpots, EngineOn)
|
||||
|
||||
-- Airbase ID.
|
||||
local AirbaseID=AirBase:GetID()
|
||||
|
||||
-- Spawn point.
|
||||
local point=Template.route.points[1]
|
||||
|
||||
-- Set ID.
|
||||
if AirBase:IsAirdrome() then
|
||||
point.airdromeId=AirbaseID
|
||||
else
|
||||
point.helipadId=AirbaseID
|
||||
point.linkUnit=AirbaseID
|
||||
end
|
||||
|
||||
if EngineOn then
|
||||
point.action=COORDINATE.WaypointAction.FromParkingAreaHot
|
||||
point.type=COORDINATE.WaypointType.TakeOffParkingHot
|
||||
else
|
||||
point.action=COORDINATE.WaypointAction.FromParkingArea
|
||||
point.type=COORDINATE.WaypointType.TakeOffParking
|
||||
end
|
||||
|
||||
for i,unit in ipairs(Template.units) do
|
||||
unit.parking_id=ParkingSpots[i]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Add a waypoint.
|
||||
-- @param #table Template The template to be modified.
|
||||
-- @param #table Waypoint Waypoint table.
|
||||
function TEMPLATE.AddWaypoint(Template, Waypoint)
|
||||
|
||||
table.insert(Template.route.points, Waypoint)
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Generic Ground Template
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
TEMPLATE.GenericGround=
|
||||
{
|
||||
["visible"] = false,
|
||||
["tasks"] = {}, -- end of ["tasks"]
|
||||
["uncontrollable"] = false,
|
||||
["task"] = "Ground Nothing",
|
||||
["route"] =
|
||||
{
|
||||
["spans"] = {}, -- end of ["spans"]
|
||||
["points"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["alt"] = 0,
|
||||
["type"] = "Turning Point",
|
||||
["ETA"] = 0,
|
||||
["alt_type"] = "BARO",
|
||||
["formation_template"] = "",
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["ETA_locked"] = true,
|
||||
["speed"] = 0,
|
||||
["action"] = "Off Road",
|
||||
["task"] =
|
||||
{
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["task"]
|
||||
["speed_locked"] = true,
|
||||
}, -- end of [1]
|
||||
}, -- end of ["points"]
|
||||
}, -- end of ["route"]
|
||||
["groupId"] = nil,
|
||||
["hidden"] = false,
|
||||
["units"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["transportable"] =
|
||||
{
|
||||
["randomTransportable"] = false,
|
||||
}, -- end of ["transportable"]
|
||||
["skill"] = "Average",
|
||||
["type"] = "Infantry AK",
|
||||
["unitId"] = nil,
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["name"] = "Infantry AK-47 Rus",
|
||||
["heading"] = 0,
|
||||
["playerCanDrive"] = false,
|
||||
}, -- end of [1]
|
||||
}, -- end of ["units"]
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["name"] = "Infantry AK-47 Rus",
|
||||
["start_time"] = 0,
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Generic Ship Template
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
TEMPLATE.GenericNaval=
|
||||
{
|
||||
["visible"] = false,
|
||||
["tasks"] = {}, -- end of ["tasks"]
|
||||
["uncontrollable"] = false,
|
||||
["route"] =
|
||||
{
|
||||
["points"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["alt"] = 0,
|
||||
["type"] = "Turning Point",
|
||||
["ETA"] = 0,
|
||||
["alt_type"] = "BARO",
|
||||
["formation_template"] = "",
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["ETA_locked"] = true,
|
||||
["speed"] = 0,
|
||||
["action"] = "Turning Point",
|
||||
["task"] =
|
||||
{
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["task"]
|
||||
["speed_locked"] = true,
|
||||
}, -- end of [1]
|
||||
}, -- end of ["points"]
|
||||
}, -- end of ["route"]
|
||||
["groupId"] = nil,
|
||||
["hidden"] = false,
|
||||
["units"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["transportable"] =
|
||||
{
|
||||
["randomTransportable"] = false,
|
||||
}, -- end of ["transportable"]
|
||||
["skill"] = "Average",
|
||||
["type"] = "TICONDEROG",
|
||||
["unitId"] = nil,
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["name"] = "Naval-1-1",
|
||||
["heading"] = 0,
|
||||
["modulation"] = 0,
|
||||
["frequency"] = 127500000,
|
||||
}, -- end of [1]
|
||||
}, -- end of ["units"]
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["name"] = "Naval-1",
|
||||
["start_time"] = 0,
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Generic Aircraft Template
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
TEMPLATE.GenericAircraft=
|
||||
{
|
||||
["groupId"] = nil,
|
||||
["name"] = "Rotary-1",
|
||||
["uncontrolled"] = false,
|
||||
["hidden"] = false,
|
||||
["task"] = "Nothing",
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["start_time"] = 0,
|
||||
["communication"] = true,
|
||||
["radioSet"] = false,
|
||||
["frequency"] = 127.5,
|
||||
["modulation"] = 0,
|
||||
["taskSelected"] = true,
|
||||
["tasks"] = {}, -- end of ["tasks"]
|
||||
["route"] =
|
||||
{
|
||||
["points"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["y"] = 0,
|
||||
["x"] = 0,
|
||||
["alt"] = 1000,
|
||||
["alt_type"] = "BARO",
|
||||
["action"] = "Turning Point",
|
||||
["type"] = "Turning Point",
|
||||
["airdromeId"] = nil,
|
||||
["task"] =
|
||||
{
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] = {}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["task"]
|
||||
["ETA"] = 0,
|
||||
["ETA_locked"] = true,
|
||||
["speed"] = 100,
|
||||
["speed_locked"] = true,
|
||||
["formation_template"] = "",
|
||||
}, -- end of [1]
|
||||
}, -- end of ["points"]
|
||||
}, -- end of ["route"]
|
||||
["units"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["name"] = "Rotary-1-1",
|
||||
["unitId"] = nil,
|
||||
["type"] = "AH-1W",
|
||||
["onboard_num"] = "050",
|
||||
["livery_id"] = "USA X Black",
|
||||
["skill"] = "High",
|
||||
["ropeLength"] = 15,
|
||||
["speed"] = 0,
|
||||
["x"] = 0,
|
||||
["y"] = 0,
|
||||
["alt"] = 10,
|
||||
["alt_type"] = "BARO",
|
||||
["heading"] = 0,
|
||||
["psi"] = 0,
|
||||
["parking"] = nil,
|
||||
["parking_id"] = nil,
|
||||
["payload"] =
|
||||
{
|
||||
["pylons"] = {}, -- end of ["pylons"]
|
||||
["fuel"] = "1250.0",
|
||||
["flare"] = 30,
|
||||
["chaff"] = 30,
|
||||
["gun"] = 100,
|
||||
}, -- end of ["payload"]
|
||||
["callsign"] =
|
||||
{
|
||||
[1] = 2,
|
||||
[2] = 1,
|
||||
[3] = 1,
|
||||
["name"] = "Springfield11",
|
||||
}, -- end of ["callsign"]
|
||||
}, -- end of [1]
|
||||
}, -- end of ["units"]
|
||||
}
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -514,7 +514,7 @@ function UTILS.PrintTableToLog(table, indent, noprint)
|
||||
env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
|
||||
end
|
||||
text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n"
|
||||
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n"
|
||||
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1), noprint).."\n"
|
||||
if not noprint then
|
||||
env.info(string.rep(" ", indent) .. "},")
|
||||
end
|
||||
@@ -2329,8 +2329,12 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
BASE:T(unit_name .. " rear cargo door is open")
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
-- ground
|
||||
local UnitDescriptor = unit:getDesc()
|
||||
local IsGroundResult = (UnitDescriptor.category == Unit.Category.GROUND_UNIT)
|
||||
|
||||
return IsGroundResult
|
||||
|
||||
end -- nil
|
||||
|
||||
@@ -4324,3 +4328,78 @@ end
|
||||
function UTILS.ScalarMult(vec, mult)
|
||||
return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult}
|
||||
end
|
||||
|
||||
--- Utilities weather class for fog mainly.
|
||||
-- @type UTILS.Weather
|
||||
UTILS.Weather = {}
|
||||
|
||||
--- Returns the current fog thickness in meters. Returns zero if fog is not present.
|
||||
function UTILS.Weather.GetFogThickness()
|
||||
return world.weather.getFogThickness()
|
||||
end
|
||||
|
||||
--- Sets the fog to the desired thickness in meters at sea level.
|
||||
-- @param #number Thickness Thickness in meters.
|
||||
-- Any fog animation will be discarded.
|
||||
-- Valid range : 100 to 5000 meters
|
||||
function UTILS.Weather.SetFogThickness(Thickness)
|
||||
local value = Thickness
|
||||
if value < 100 then value = 100
|
||||
elseif value > 5000 then value = 5000 end
|
||||
return world.weather.setFogThickness(value)
|
||||
end
|
||||
|
||||
--- Removes the fog.
|
||||
function UTILS.Weather.RemoveFog()
|
||||
return world.weather.setFogThickness(0)
|
||||
end
|
||||
|
||||
--- Gets the maximum visibility distance of the current fog setting.
|
||||
-- Returns 0 if no fog is present.
|
||||
function UTILS.Weather.GetFogVisibilityDistanceMax()
|
||||
return world.weather.getFogVisibilityDistance()
|
||||
end
|
||||
|
||||
--- Sets the maximum visibility at sea level in meters.
|
||||
-- @param #number Thickness Thickness in meters.
|
||||
-- Limit: 100 to 100000
|
||||
function UTILS.Weather.SetFogVisibilityDistance(Thickness)
|
||||
local value = Thickness
|
||||
if value < 100 then value = 100
|
||||
elseif value > 100000 then value = 100000 end
|
||||
return world.weather.setFogVisibilityDistance(value)
|
||||
end
|
||||
|
||||
--- Uses data from the passed table to change the fog visibility and thickness over a desired timeframe. This allows for a gradual increase/decrease of fog values rather than abruptly applying the values.
|
||||
-- Animation Key Format: {time, visibility, thickness}
|
||||
-- @param #table AnimationKeys Table of AnimationKey tables
|
||||
-- @usage
|
||||
-- Time: in seconds 0 to infinity
|
||||
-- Time is relative to when the function was called. Time value for each key must be larger than the previous key. If time is set to 0 then the fog will be applied to the corresponding visibility and thickness values at that key. Any time value greater than 0 will result in the current fog being inherited and changed to the first key.
|
||||
-- Visibility: in meters 100 to 100000
|
||||
-- Thickness: in meters 100 to 5000
|
||||
-- The speed at which the visibility and thickness changes is based on the time between keys and the values that visibility and thickness are being set to.
|
||||
--
|
||||
-- When the function is passed an empty table {} or nil the fog animation will be discarded and whatever the current thickness and visibility are set to will remain.
|
||||
--
|
||||
-- The following will set the fog in the mission to disappear in 1 minute.
|
||||
--
|
||||
-- UTILS.Weather.SetFogAnimation({ {60, 0, 0} })
|
||||
--
|
||||
-- The following will take 1 hour to get to the first fog setting, it will maintain that fog setting for another hour, then lightly removes the fog over the 2nd and 3rd hour, the completely removes the fog after 3 hours and 3 minutes from when the function was called.
|
||||
--
|
||||
-- UTILS.Weather.SetFogAnimation({
|
||||
-- {3600, 10000, 3000}, -- one hour to get to that fog setting
|
||||
-- {7200, 10000, 3000}, -- will maintain for 2 hours
|
||||
-- {10800, 20000, 2000}, -- at 3 hours visibility will have been increased while thickness decreases slightly
|
||||
-- {12600, 0, 0}, -- at 3:30 after the function was called the fog will be completely removed.
|
||||
-- })
|
||||
--
|
||||
function UTILS.Weather.SetFogAnimation(AnimationKeys)
|
||||
return world.weather.setFogAnimation(AnimationKeys)
|
||||
end
|
||||
|
||||
--- The fog animation will be discarded and whatever the current thickness and visibility are set to will remain
|
||||
function UTILS.Weather.StopFogAnimation()
|
||||
return world.weather.setFogAnimation({})
|
||||
end
|
||||
|
||||
@@ -823,38 +823,57 @@ AIRBASE.Kola = {
|
||||
--- Airbases of the Afghanistan map
|
||||
--
|
||||
-- * AIRBASE.Afghanistan.Bost
|
||||
-- * AIRBASE.Afghanistan.Bagram
|
||||
-- * AIRBASE.Afghanistan.Bamyan
|
||||
-- * AIRBASE.Afghanistan.Camp_Bastion
|
||||
-- * AIRBASE.Afghanistan.Camp_Bastion_Heliport
|
||||
-- * AIRBASE.Afghanistan.Chaghcharan
|
||||
-- * AIRBASE.Afghanistan.Dwyer
|
||||
-- * AIRBASE.Afghanistan.Farah
|
||||
-- * AIRBASE.Afghanistan.Herat
|
||||
-- * AIRBASE.Afghanistan.Gardez
|
||||
-- * AIRBASE.Afghanistan.Ghazni_Heliport
|
||||
-- * AIRBASE.Afghanistan.Jalalabad
|
||||
-- * AIRBASE.Afghanistan.Kabul
|
||||
-- * AIRBASE.Afghanistan.Kandahar
|
||||
-- * AIRBASE.Afghanistan.Kandahar_Heliport
|
||||
-- * AIRBASE.Afghanistan.Khost
|
||||
-- * AIRBASE.Afghanistan.Khost_Heliport
|
||||
-- * AIRBASE.Afghanistan.Maymana_Zahiraddin_Faryabi
|
||||
-- * AIRBASE.Afghanistan.Nimroz
|
||||
-- * AIRBASE.Afghanistan.Qala_i_Naw
|
||||
-- * AIRBASE.Afghanistan.Shindand
|
||||
-- * AIRBASE.Afghanistan.Shindand_Heliport
|
||||
-- * AIRBASE.Afghanistan.Tarinkot
|
||||
-- * AIRBASE.Afghanistan.Urgoon_Heliport
|
||||
--
|
||||
-- @field Afghanistan
|
||||
AIRBASE.Afghanistan = {
|
||||
["Bagram"] = "Bagram",
|
||||
["Bamyan"] = "Bamyan",
|
||||
["Bost"] = "Bost",
|
||||
["Camp_Bastion"] = "Camp Bastion",
|
||||
["Camp_Bastion_Heliport"] = "Camp Bastion Heliport",
|
||||
["Chaghcharan"] = "Chaghcharan",
|
||||
["Dwyer"] = "Dwyer",
|
||||
["Farah"] = "Farah",
|
||||
["Gardez"] = "Gardez",
|
||||
["Ghazni_Heliport"] = "Ghazni Heliport",
|
||||
["Herat"] = "Herat",
|
||||
["Jalalabad"] = "Jalalabad",
|
||||
["Kabul"] = "Kabul",
|
||||
["Kandahar"] = "Kandahar",
|
||||
["Kandahar_Heliport"] = "Kandahar Heliport",
|
||||
["Khost"] = "Khost",
|
||||
["Khost_Heliport"] = "Khost Heliport",
|
||||
["Maymana_Zahiraddin_Faryabi"] = "Maymana Zahiraddin Faryabi",
|
||||
["Nimroz"] = "Nimroz",
|
||||
["Qala_i_Naw"] = "Qala i Naw",
|
||||
["Sharana"] = "Sharana",
|
||||
["Shindand"] = "Shindand",
|
||||
["Shindand_Heliport"] = "Shindand Heliport",
|
||||
["Tarinkot"] = "Tarinkot",
|
||||
["Urgoon_Heliport"] = "Urgoon Heliport",
|
||||
}
|
||||
|
||||
--- Airbases of the Iraq map
|
||||
@@ -926,11 +945,12 @@ AIRBASE.Iraq = {
|
||||
-- @field #number HelicopterOnly 40: Special spots for Helicopers.
|
||||
-- @field #number Shelter 68: Hardened Air Shelter. Currently only on Caucaus map.
|
||||
-- @field #number OpenMed 72: Open/Shelter air airplane only.
|
||||
-- @field #number SmallSizeFigher 100: Tight spots for smaller type fixed wing aircraft, like the F-16. Example of these spots: 04, 05, 06 on Muwaffaq_Salti. A Viper sized plane can spawn here, but an A-10 or Strike Eagle can't
|
||||
-- @field #number OpenBig 104: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there.
|
||||
-- @field #number OpenMedOrBig 176: Combines OpenMed and OpenBig spots.
|
||||
-- @field #number HelicopterUsable 216: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- @field #number FighterAircraft 244: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
|
||||
-- @field #number SmallSizeFigher 100: Tight spots for smaller type fixed wing aircraft, like the F-16. Example of these spots: 04, 05, 06 on Muwaffaq_Salti. A Viper sized plane can spawn here, but an A-10 or Strike Eagle can't
|
||||
-- @field #number FighterAircraft 244: Combines Shelter, OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
|
||||
-- @field #number FighterAircraftSmall 344: Combines Shelter, SmallsizeFighter, OpenMed and OpenBig spots. So effectively all spots usable by small fixed wing aircraft.
|
||||
AIRBASE.TerminalType = {
|
||||
Runway=16,
|
||||
HelicopterOnly=40,
|
||||
@@ -941,6 +961,7 @@ AIRBASE.TerminalType = {
|
||||
OpenMedOrBig=176,
|
||||
HelicopterUsable=216,
|
||||
FighterAircraft=244,
|
||||
FighterAircraftSmall=344,
|
||||
}
|
||||
|
||||
--- Status of a parking spot.
|
||||
@@ -992,7 +1013,7 @@ function AIRBASE:Register(AirbaseName)
|
||||
|
||||
-- Debug info.
|
||||
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
|
||||
|
||||
|
||||
-- Category.
|
||||
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
||||
|
||||
@@ -1007,6 +1028,7 @@ if self.category==Airbase.Category.AIRDROME then
|
||||
self.isAirdrome=true
|
||||
elseif self.category==Airbase.Category.HELIPAD or self.descriptors.typeName=="FARP_SINGLE_01" then
|
||||
self.isHelipad=true
|
||||
self.category=Airbase.Category.HELIPAD
|
||||
elseif self.category==Airbase.Category.SHIP then
|
||||
self.isShip=true
|
||||
-- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects()
|
||||
@@ -1022,21 +1044,35 @@ end
|
||||
|
||||
-- Init Runways.
|
||||
self:_InitRunways()
|
||||
|
||||
|
||||
-- Number of runways
|
||||
local Nrunways=#self.runways
|
||||
|
||||
-- Set the active runways based on wind direction.
|
||||
if self.isAirdrome then
|
||||
if Nrunways>0 then
|
||||
self:SetActiveRunway()
|
||||
end
|
||||
|
||||
-- Init parking spots.
|
||||
self:_InitParkingSpots()
|
||||
|
||||
-- Some heliports identify as airdromes in the airbase category. This is buggy in the descriptors category but also in the getCategory() and getCategoryEx() functions.
|
||||
-- Well, thinking about it, this is actually not that "buggy" since these are really helicopter airdromes, which do not have an automatic parking spot routine.
|
||||
-- I am still changing the category but marking it as airdrome and heliport at the same time via isAirdrome=true and isHelipad=true (important in SPAWN.SpawnAtAirbase).
|
||||
-- The main reason for changing the category is to be able to filter airdromes from helipads, e.g. in SET_AIRBASE.
|
||||
if self.category==Airbase.Category.AIRDROME and (Nrunways==0 or self.NparkingTotal==self.NparkingTerminal[AIRBASE.TerminalType.HelicopterOnly]) then
|
||||
--self:E(string.format("WARNING: %s identifies as airdrome (category=0) but has no runways or just helo parking ==> will change to helipad (category=1)", self.AirbaseName))
|
||||
self.category=Airbase.Category.HELIPAD
|
||||
self.isAirdrome=true
|
||||
self.isHelipad=true
|
||||
end
|
||||
|
||||
-- Get 2D position vector.
|
||||
local vec2=self:GetVec2()
|
||||
|
||||
-- Init coordinate.
|
||||
self:GetCoordinate()
|
||||
|
||||
|
||||
-- Storage.
|
||||
self.storage=_DATABASE:AddStorage(AirbaseName)
|
||||
|
||||
@@ -1059,6 +1095,46 @@ end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the category of this airbase. This is only a debug function because DCS 2.9 incorrectly returns heliports as airdromes.
|
||||
-- @param #AIRBASE self
|
||||
function AIRBASE:_GetCategory()
|
||||
|
||||
local name=self.AirbaseName
|
||||
|
||||
local static=StaticObject.getByName(name)
|
||||
local airbase=Airbase.getByName(name)
|
||||
local unit=Unit.getByName(name)
|
||||
|
||||
local text=string.format("\n=====================================================")
|
||||
text=text..string.format("\nAirbase %s:", name)
|
||||
if static then
|
||||
local oc, uc=static:getCategory()
|
||||
local ex=static:getCategoryEx()
|
||||
text=text..string.format("\nSTATIC: oc=%d, uc=%d, ex=%d", oc, uc, ex)
|
||||
--text=text..UTILS.PrintTableToLog(static:getDesc(), nil, true)
|
||||
text=text..string.format("\n--------------------------------------------------")
|
||||
end
|
||||
if unit then
|
||||
local oc, uc=unit:getCategory()
|
||||
local ex=unit:getCategoryEx()
|
||||
text=text..string.format("\nUNIT: oc=%d, uc=%d, ex=%d", oc, uc, ex)
|
||||
--text=text..UTILS.PrintTableToLog(unit:getDesc(), nil, true)
|
||||
text=text..string.format("\n--------------------------------------------------")
|
||||
end
|
||||
if airbase then
|
||||
local oc, uc=airbase:getCategory()
|
||||
local ex=airbase:getCategoryEx()
|
||||
text=text..string.format("\nAIRBASE: oc=%d, uc=%d, ex=%d", oc, uc, ex)
|
||||
text=text..string.format("\n--------------------------------------------------")
|
||||
text=text..UTILS.PrintTableToLog(airbase:getDesc(), nil, true)
|
||||
end
|
||||
|
||||
text=text..string.format("\n=====================================================")
|
||||
|
||||
|
||||
env.info(text)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Reference methods
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1582,7 +1658,7 @@ function AIRBASE:_InitParkingSpots()
|
||||
self.NparkingTotal=self.NparkingTotal+1
|
||||
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if self._CheckTerminalType(terminalType, park.TerminalType) then
|
||||
if self._CheckTerminalType(park.TerminalType, terminalType) then
|
||||
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
|
||||
end
|
||||
end
|
||||
@@ -1590,6 +1666,9 @@ function AIRBASE:_InitParkingSpots()
|
||||
self.parkingByID[park.TerminalID]=park
|
||||
table.insert(self.parking, park)
|
||||
end
|
||||
|
||||
-- Runways are not included in total number of parking spots
|
||||
self.NparkingTotal=self.NparkingTotal-self.NparkingTerminal[AIRBASE.TerminalType.Runway]
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -2013,9 +2092,13 @@ function AIRBASE._CheckTerminalType(Term_Type, termtype)
|
||||
match=true
|
||||
end
|
||||
elseif termtype==AIRBASE.TerminalType.FighterAircraft then
|
||||
if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter or Term_Type==AIRBASE.TerminalType.SmallSizeFighter then
|
||||
if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter then
|
||||
match=true
|
||||
end
|
||||
elseif termtype==AIRBASE.TerminalType.FighterAircraftSmall then
|
||||
if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter or Term_Type==AIRBASE.TerminalType.SmallSizeFighter then
|
||||
match=true
|
||||
end
|
||||
end
|
||||
|
||||
return match
|
||||
@@ -2073,11 +2156,6 @@ function AIRBASE:_InitRunways(IncludeInverse)
|
||||
-- Runway table.
|
||||
local Runways={}
|
||||
|
||||
if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
|
||||
self.runways={}
|
||||
return {}
|
||||
end
|
||||
|
||||
--- Function to create a runway data table.
|
||||
local function _createRunway(name, course, width, length, center)
|
||||
|
||||
@@ -2163,7 +2241,7 @@ function AIRBASE:_InitRunways(IncludeInverse)
|
||||
-- Debug info.
|
||||
self:T2(runways)
|
||||
|
||||
if runways then
|
||||
if runways and #runways>0 then
|
||||
|
||||
-- Loop over runways.
|
||||
for _,rwy in pairs(runways) do
|
||||
@@ -2196,6 +2274,12 @@ function AIRBASE:_InitRunways(IncludeInverse)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- No runways
|
||||
self.runways={}
|
||||
return {}
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -973,7 +973,7 @@ end
|
||||
-- @param #number Frequency Radio frequency in MHz.
|
||||
-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`.
|
||||
-- @param #number Power (Optional) Power of the Radio in Watts. Defaults to 10.
|
||||
-- @param #UnitID UnitID (Optional, if your object is a UNIT) The UNIT ID this is for.
|
||||
-- @param #number UnitID (Optional, if your object is a UNIT) The UNIT ID this is for.
|
||||
-- @param #number Delay (Optional) Delay in seconds before the frequency is set. Default is immediately.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:CommandSetFrequencyForUnit(Frequency,Modulation,Power,UnitID,Delay)
|
||||
@@ -994,6 +994,65 @@ function CONTROLLABLE:CommandSetFrequencyForUnit(Frequency,Modulation,Power,Unit
|
||||
return self
|
||||
end
|
||||
|
||||
--- [AIR] Set smoke on or off. See [DCS command smoke on off](https://wiki.hoggitworld.com/view/DCS_command_smoke_on_off)
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #boolean OnOff Set to true for on and false for off. Defaults to true.
|
||||
-- @param #number Delay (Optional) Delay the command by this many seconds.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:CommandSmokeOnOff(OnOff, Delay)
|
||||
local switch = (OnOff == nil) and true or OnOff
|
||||
local command = {
|
||||
id = 'SMOKE_ON_OFF',
|
||||
params = {
|
||||
value = switch
|
||||
}
|
||||
}
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil,self.CommandSmokeOnOff,{self,switch},Delay)
|
||||
else
|
||||
self:SetCommand(command)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [AIR] Set smoke on. See [DCS command smoke on off](https://wiki.hoggitworld.com/view/DCS_command_smoke_on_off)
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Delay (Optional) Delay the command by this many seconds.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:CommandSmokeON(Delay)
|
||||
local command = {
|
||||
id = 'SMOKE_ON_OFF',
|
||||
params = {
|
||||
value = true
|
||||
}
|
||||
}
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil,self.CommandSmokeON,{self},Delay)
|
||||
else
|
||||
self:SetCommand(command)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [AIR] Set smoke off. See [DCS command smoke on off](https://wiki.hoggitworld.com/view/DCS_command_smoke_on_off)
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Delay (Optional) Delay the command by this many seconds.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:CommandSmokeOFF(Delay)
|
||||
local command = {
|
||||
id = 'SMOKE_ON_OFF',
|
||||
params = {
|
||||
value = false
|
||||
}
|
||||
}
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil,self.CommandSmokeOFF,{self},Delay)
|
||||
else
|
||||
self:SetCommand(command)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set EPLRS data link on/off.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #boolean SwitchOnOff If true (or nil) switch EPLRS on. If false switch off.
|
||||
@@ -1777,8 +1836,6 @@ function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation,
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
-- EN-ACT_ROUTE TASKS FOR AIRBORNE CONTROLLABLES
|
||||
|
||||
--- (AIR) Engaging targets of defined types.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param DCS#Distance Distance Maximal distance from the target to a route leg. If the target is on a greater distance it will be ignored.
|
||||
@@ -4263,6 +4320,9 @@ function CONTROLLABLE:RelocateGroundRandomInRadius( speed, radius, onroad, short
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local _coord = self:GetCoordinate()
|
||||
if not _coord then
|
||||
return self
|
||||
end
|
||||
local _radius = radius or 500
|
||||
local _speed = speed or 20
|
||||
local _tocoord = _coord:GetRandomCoordinateInRadius( _radius, 100 )
|
||||
@@ -4312,7 +4372,7 @@ function CONTROLLABLE:OptionDisperseOnAttack( Seconds )
|
||||
end
|
||||
|
||||
--- Returns if the unit is a submarine.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #boolean Submarines attributes result.
|
||||
function CONTROLLABLE:IsSubmarine()
|
||||
self:F2()
|
||||
@@ -5638,22 +5698,21 @@ end
|
||||
--- [GROUND] Create and enable a new IR Marker for the given controllable UNIT or GROUP.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #boolean EnableImmediately (Optionally) If true start up the IR Marker immediately. Else you need to call `myobject:EnableIRMarker()` later on.
|
||||
-- @param #number Runtime (Optionally) Run this IR Marker for the given number of seconds, then stop. Use in conjunction with EnableImmediately.
|
||||
-- @param #number Runtime (Optionally) Run this IR Marker for the given number of seconds, then stop. Use in conjunction with EnableImmediately. Defaults to 60 seconds.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:NewIRMarker(EnableImmediately, Runtime)
|
||||
--sefl:F("NewIRMarker")
|
||||
if self.ClassName == "GROUP" then
|
||||
self:T2("NewIRMarker")
|
||||
if self:IsInstanceOf("GROUP") then
|
||||
if self.IRMarkerGroup == true then return end
|
||||
self.IRMarkerGroup = true
|
||||
self.IRMarkerUnit = false
|
||||
elseif self.ClassName == "UNIT" then
|
||||
elseif self:IsInstanceOf("UNIT") then
|
||||
if self.IRMarkerUnit == true then return end
|
||||
self.IRMarkerGroup = false
|
||||
self.IRMarkerUnit = true
|
||||
end
|
||||
|
||||
self.spot = nil
|
||||
self.timer = nil
|
||||
self.stoptimer = nil
|
||||
|
||||
self.Runtime = Runtime or 60
|
||||
if EnableImmediately and EnableImmediately == true then
|
||||
self:EnableIRMarker(Runtime)
|
||||
end
|
||||
@@ -5666,19 +5725,23 @@ end
|
||||
-- @param #number Runtime (Optionally) Run this IR Marker for the given number of seconds, then stop. Else run until you call `myobject:DisableIRMarker()`.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:EnableIRMarker(Runtime)
|
||||
--sefl:F("EnableIRMarker")
|
||||
self:T2("EnableIRMarker")
|
||||
if self.IRMarkerGroup == nil then
|
||||
self:NewIRMarker(true,Runtime)
|
||||
return
|
||||
end
|
||||
|
||||
if (self.IRMarkerGroup == true) then
|
||||
self:EnableIRMarkerForGroup()
|
||||
if self:IsInstanceOf("GROUP") then
|
||||
self:EnableIRMarkerForGroup(Runtime)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if self.timer and self.timer:IsRunning() then return self end
|
||||
|
||||
local Runtime = Runtime or self.Runtime
|
||||
self.timer = TIMER:New(CONTROLLABLE._MarkerBlink, self)
|
||||
self.timer:Start(nil, 1 - math.random(1, 5) / 10 / 2, Runtime) -- start randomized
|
||||
self.IRMarkerUnit = true
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -5687,33 +5750,42 @@ end
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:DisableIRMarker()
|
||||
--sefl:F("DisableIRMarker")
|
||||
if (self.IRMarkerGroup == true) then
|
||||
self:T2("DisableIRMarker")
|
||||
if self:IsInstanceOf("GROUP") then
|
||||
self:DisableIRMarkerForGroup()
|
||||
return
|
||||
end
|
||||
|
||||
if self.spot then
|
||||
self.spot:destroy()
|
||||
self.spot = nil
|
||||
|
||||
if self.spot then
|
||||
self.spot = nil
|
||||
end
|
||||
if self.timer and self.timer:IsRunning() then
|
||||
self.timer:Stop()
|
||||
self.timer = nil
|
||||
end
|
||||
|
||||
if self:IsInstanceOf("GROUP") then
|
||||
self.IRMarkerGroup = nil
|
||||
elseif self:IsInstanceOf("UNIT") then
|
||||
self.IRMarkerUnit = nil
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- [GROUND] Enable the IR markers for a whole group.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Runtime Runtime of the marker in seconds
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:EnableIRMarkerForGroup()
|
||||
--sefl:F("EnableIRMarkerForGroup")
|
||||
if self.ClassName == "GROUP" then
|
||||
function CONTROLLABLE:EnableIRMarkerForGroup(Runtime)
|
||||
self:T2("EnableIRMarkerForGroup")
|
||||
if self:IsInstanceOf("GROUP")
|
||||
then
|
||||
local units = self:GetUnits() or {}
|
||||
for _,_unit in pairs(units) do
|
||||
_unit:EnableIRMarker()
|
||||
_unit:EnableIRMarker(Runtime)
|
||||
end
|
||||
self.IRMarkerGroup = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -5722,21 +5794,43 @@ end
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:DisableIRMarkerForGroup()
|
||||
--sefl:F("DisableIRMarkerForGroup")
|
||||
if self.ClassName == "GROUP" then
|
||||
self:T2("DisableIRMarkerForGroup")
|
||||
if self:IsInstanceOf("GROUP") then
|
||||
local units = self:GetUnits() or {}
|
||||
for _,_unit in pairs(units) do
|
||||
_unit:DisableIRMarker()
|
||||
end
|
||||
self.IRMarkerGroup = nil
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [GROUND] Check if an IR Spot exists.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #boolean outcome
|
||||
function CONTROLLABLE:HasIRMarker()
|
||||
self:T2("HasIRMarker")
|
||||
if self:IsInstanceOf("GROUP") then
|
||||
local units = self:GetUnits() or {}
|
||||
for _,_unit in pairs(units) do
|
||||
if _unit.timer and _unit.timer:IsRunning() then return true end
|
||||
end
|
||||
elseif self.timer and self.timer:IsRunning() then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
--- [Internal] This method is called by the scheduler to blink the IR marker.
|
||||
function CONTROLLABLE._StopSpot(spot)
|
||||
if spot then
|
||||
spot:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] This method is called by the scheduler after enabling the IR marker.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:_MarkerBlink()
|
||||
--sefl:F("_MarkerBlink")
|
||||
self:T2("_MarkerBlink")
|
||||
if self:IsAlive() ~= true then
|
||||
self:DisableIRMarker()
|
||||
return
|
||||
@@ -5747,13 +5841,17 @@ function CONTROLLABLE:_MarkerBlink()
|
||||
local _, _, unitBBHeight, _ = self:GetObjectSize()
|
||||
local unitPos = self:GetPositionVec3()
|
||||
|
||||
self.spot = Spot.createInfraRed(
|
||||
self.DCSUnit,
|
||||
{ x = 0, y = (unitBBHeight + 1), z = 0 },
|
||||
{ x = unitPos.x, y = (unitPos.y + unitBBHeight), z = unitPos.z }
|
||||
)
|
||||
|
||||
local offTimer = TIMER:New(function() if self.spot then self.spot:destroy() end end)
|
||||
offTimer:Start(0.5)
|
||||
if self.timer:IsRunning() then
|
||||
self:T2("Create Spot")
|
||||
local spot = Spot.createInfraRed(
|
||||
self.DCSUnit,
|
||||
{ x = 0, y = (unitBBHeight + 1), z = 0 },
|
||||
{ x = unitPos.x, y = (unitPos.y + unitBBHeight), z = unitPos.z }
|
||||
)
|
||||
self.spot = spot
|
||||
local offTimer = nil
|
||||
local offTimer = TIMER:New(CONTROLLABLE._StopSpot, spot)
|
||||
offTimer:Start(0.5)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
--
|
||||
-- ## Main Features:
|
||||
--
|
||||
-- * Convenient access to DCS API functions
|
||||
-- * Convenient access to Ground Crew created cargo items.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Wrapper/Storage).
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Applevangelist**
|
||||
-- ### Author: **Applevangelist**; additional checks **Chesster**
|
||||
--
|
||||
-- ===
|
||||
-- @module Wrapper.DynamicCargo
|
||||
@@ -124,7 +124,7 @@ DYNAMICCARGO.AircraftDimensions = {
|
||||
|
||||
--- DYNAMICCARGO class version.
|
||||
-- @field #string version
|
||||
DYNAMICCARGO.version="0.0.5"
|
||||
DYNAMICCARGO.version="0.0.7"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -183,7 +183,7 @@ end
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return DCS static object
|
||||
function DYNAMICCARGO:GetDCSObject()
|
||||
local DCSStatic = Unit.getByName( self.StaticName )
|
||||
local DCSStatic = StaticObject.getByName( self.StaticName ) or Unit.getByName( self.StaticName )
|
||||
if DCSStatic then
|
||||
return DCSStatic
|
||||
end
|
||||
@@ -227,7 +227,7 @@ end
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #boolean Outcome
|
||||
function DYNAMICCARGO:IsUnloaded()
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.REMOVED then
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.UNLOADED then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
@@ -238,7 +238,7 @@ end
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #boolean Outcome
|
||||
function DYNAMICCARGO:IsRemoved()
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.UNLOADED then
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.REMOVED then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
@@ -376,6 +376,33 @@ end
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- [Internal] _Get helo hovering intel
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @param Wrapper.Unit#UNIT Unit The Unit to test
|
||||
-- @param #number ropelength Ropelength to test
|
||||
-- @return #boolean Outcome
|
||||
function DYNAMICCARGO:_HeloHovering(Unit,ropelength)
|
||||
local DCSUnit = Unit:GetDCSObject() --DCS#Unit
|
||||
local hovering = false
|
||||
local Height = 0
|
||||
if DCSUnit then
|
||||
local UnitInAir = DCSUnit:inAir()
|
||||
local UnitCategory = DCSUnit:getDesc().category
|
||||
if UnitInAir == true and UnitCategory == 1 then
|
||||
local VelocityVec3 = DCSUnit:getVelocity()
|
||||
local Velocity = UTILS.VecNorm(VelocityVec3)
|
||||
local Coordinate = DCSUnit:getPoint()
|
||||
local LandHeight = land.getHeight({ x = Coordinate.x, y = Coordinate.z })
|
||||
Height = Coordinate.y - LandHeight
|
||||
if Velocity < 1 and Height <= ropelength and Height > 6 then -- hover lower than ropelength but higher than the normal FARP height.
|
||||
hovering = true
|
||||
end
|
||||
end
|
||||
return hovering, Height
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- [Internal] _Get Possible Player Helo Nearby
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @param Core.Point#COORDINATE pos
|
||||
@@ -393,30 +420,37 @@ function DYNAMICCARGO:_GetPossibleHeloNearby(pos,loading)
|
||||
local name = helo:GetPlayerName() or _DATABASE:_FindPlayerNameByUnitName(helo:GetName()) or "None"
|
||||
self:T(self.lid.." Checking: "..name)
|
||||
local hpos = helo:GetCoordinate()
|
||||
-- TODO Unloading via sling load?
|
||||
--local inair = hpos.y-hpos:GetLandHeight() > 4.5 and true or false -- Standard FARP is 4.5m
|
||||
local inair = helo:InAir()
|
||||
self:T(self.lid.." InAir: AGL/InAir: "..hpos.y-hpos:GetLandHeight().."/"..tostring(inair))
|
||||
-- TODO Check unloading via sling load?
|
||||
local typename = helo:GetTypeName()
|
||||
if hpos and typename and inair == false then
|
||||
local dimensions = DYNAMICCARGO.AircraftDimensions[typename]
|
||||
if dimensions then
|
||||
local delta2D = hpos:Get2DDistance(pos)
|
||||
local delta3D = hpos:Get3DDistance(pos)
|
||||
if self.testing then
|
||||
self:T(string.format("Cargo relative position: 2D %dm | 3D %dm",delta2D,delta3D))
|
||||
self:T(string.format("Helo dimension: length %dm | width %dm | rope %dm",dimensions.length,dimensions.width,dimensions.ropelength))
|
||||
end
|
||||
if loading~=true and delta2D > dimensions.length or delta2D > dimensions.width or delta3D > dimensions.ropelength then
|
||||
success = true
|
||||
Helo = helo
|
||||
Playername = name
|
||||
end
|
||||
if loading == true and delta2D < dimensions.length or delta2D < dimensions.width or delta3D < dimensions.ropelength then
|
||||
success = true
|
||||
Helo = helo
|
||||
Playername = name
|
||||
end
|
||||
local dimensions = DYNAMICCARGO.AircraftDimensions[typename]
|
||||
local hovering, height = self:_HeloHovering(helo,dimensions.ropelength)
|
||||
local helolanded = not helo:InAir()
|
||||
self:T(self.lid.." InAir: AGL/Hovering: "..hpos.y-hpos:GetLandHeight().."/"..tostring(hovering))
|
||||
if hpos and typename and dimensions then
|
||||
local delta2D = hpos:Get2DDistance(pos)
|
||||
local delta3D = hpos:Get3DDistance(pos)
|
||||
if self.testing then
|
||||
self:T(string.format("Cargo relative position: 2D %dm | 3D %dm",delta2D,delta3D))
|
||||
self:T(string.format("Helo dimension: length %dm | width %dm | rope %dm",dimensions.length,dimensions.width,dimensions.ropelength))
|
||||
self:T(string.format("Helo hovering: %s at %dm",tostring(hovering),height))
|
||||
end
|
||||
-- unloading from ground
|
||||
if loading~=true and (delta2D > dimensions.length or delta2D > dimensions.width) and helolanded then -- Theoretically the cargo could still be attached to the sling if landed next to the cargo. But once moved again it would go back into loaded state once lifted again.
|
||||
success = true
|
||||
Helo = helo
|
||||
Playername = name
|
||||
end
|
||||
-- unloading from hover/rope
|
||||
if loading~=true and delta3D > dimensions.ropelength then
|
||||
success = true
|
||||
Helo = helo
|
||||
Playername = name
|
||||
end
|
||||
-- loading
|
||||
if loading == true and ((delta2D < dimensions.length and delta2D < dimensions.width and helolanded) or (delta3D == dimensions.ropelength and helo:InAir())) then -- Loaded via ground or sling
|
||||
success = true
|
||||
Helo = helo
|
||||
Playername = name
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -434,20 +468,22 @@ function DYNAMICCARGO:_UpdatePosition()
|
||||
self:T(string.format("Cargo position: x=%d, y=%d, z=%d",pos.x,pos.y,pos.z))
|
||||
self:T(string.format("Last position: x=%d, y=%d, z=%d",self.LastPosition.x,self.LastPosition.y,self.LastPosition.z))
|
||||
end
|
||||
if UTILS.Round(UTILS.VecDist3D(pos,self.LastPosition),2) > 0.5 then
|
||||
if UTILS.Round(UTILS.VecDist3D(pos,self.LastPosition),2) > 0.5 then -- This checks if the cargo has moved more than 0.5m since last check. If so then the cargo is loaded
|
||||
---------------
|
||||
-- LOAD Cargo
|
||||
---------------
|
||||
if self.CargoState == DYNAMICCARGO.State.NEW then
|
||||
local isloaded, client, playername = self:_GetPossibleHeloNearby(pos,true)
|
||||
if self.CargoState == DYNAMICCARGO.State.NEW or self.CargoState == DYNAMICCARGO.State.UNLOADED then
|
||||
local isloaded, client, playername = self:_GetPossibleHeloNearby(pos,true)
|
||||
self:T(self.lid.." moved! NEW -> LOADED by "..tostring(playername))
|
||||
self.CargoState = DYNAMICCARGO.State.LOADED
|
||||
self.Owner = playername
|
||||
_DATABASE:CreateEventDynamicCargoLoaded(self)
|
||||
_DATABASE:CreateEventDynamicCargoLoaded(self)
|
||||
end
|
||||
---------------
|
||||
-- UNLOAD Cargo
|
||||
---------------
|
||||
elseif self.CargoState == DYNAMICCARGO.State.LOADED then
|
||||
---------------
|
||||
-- If the cargo is stationary then we need to end this condition here to check whether it is unloaded or still onboard or still hooked if anyone can hover that precisly
|
||||
elseif self.CargoState == DYNAMICCARGO.State.LOADED then
|
||||
-- TODO add checker if we are in flight somehow
|
||||
-- ensure not just the helo is moving
|
||||
local count = _DYNAMICCARGO_HELOS:CountAlive()
|
||||
@@ -459,26 +495,19 @@ function DYNAMICCARGO:_UpdatePosition()
|
||||
local isunloaded = true
|
||||
local client
|
||||
local playername = self.Owner
|
||||
if count > 0 and (agl > 0 or self.testing) then
|
||||
self:T(self.lid.." Possible alive helos: "..count or -1)
|
||||
if agl ~= 0 or self.testing then
|
||||
isunloaded, client, playername = self:_GetPossibleHeloNearby(pos,false)
|
||||
end
|
||||
if count > 0 then
|
||||
self:T(self.lid.." Possible alive helos: "..count or -1)
|
||||
isunloaded, client, playername = self:_GetPossibleHeloNearby(pos,false)
|
||||
if isunloaded then
|
||||
self:T(self.lid.." moved! LOADED -> UNLOADED by "..tostring(playername))
|
||||
self.CargoState = DYNAMICCARGO.State.UNLOADED
|
||||
self.Owner = playername
|
||||
_DATABASE:CreateEventDynamicCargoUnloaded(self)
|
||||
end
|
||||
elseif count > 0 and agl == 0 then
|
||||
self:T(self.lid.." moved! LOADED -> UNLOADED by "..tostring(playername))
|
||||
self.CargoState = DYNAMICCARGO.State.UNLOADED
|
||||
self.Owner = playername
|
||||
_DATABASE:CreateEventDynamicCargoUnloaded(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.LastPosition = pos
|
||||
end
|
||||
--end
|
||||
else
|
||||
---------------
|
||||
-- REMOVED Cargo
|
||||
|
||||
@@ -757,7 +757,11 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean If true, group is associated with a client or player slot.
|
||||
function GROUP:IsPlayer()
|
||||
return self:GetUnit(1):IsPlayer()
|
||||
local unit = self:GetUnit(1)
|
||||
if unit then
|
||||
return unit:IsPlayer()
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Returns the UNIT wrapper object with number UnitNumber. If it doesn't exist, tries to return the next available unit.
|
||||
@@ -1175,9 +1179,9 @@ function GROUP:GetAverageVec3()
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission.
|
||||
--- Returns a COORDINATE object indicating the point in 2D of the first UNIT of the GROUP within the mission.
|
||||
-- @param #GROUP self
|
||||
-- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP.
|
||||
-- @return Core.Point#COORDINATE The 3D point vector of the first DCS Unit of the GROUP.
|
||||
-- @return #nil The first UNIT is not existing or alive.
|
||||
function GROUP:GetPointVec2()
|
||||
--self:F2(self.GroupName)
|
||||
@@ -1190,7 +1194,7 @@ function GROUP:GetPointVec2()
|
||||
return FirstUnitPointVec2
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetPointVec2", Group = self, Alive = self:IsAlive() } )
|
||||
BASE:E( { "Cannot get COORDINATE", Group = self, Alive = self:IsAlive() } )
|
||||
|
||||
return nil
|
||||
end
|
||||
@@ -1221,7 +1225,17 @@ end
|
||||
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
|
||||
function GROUP:GetCoordinate()
|
||||
|
||||
local Units = self:GetUnits() or {}
|
||||
-- First try to get the 3D vector of the group. This uses
|
||||
local vec3=self:GetVec3()
|
||||
local coord
|
||||
if vec3 then
|
||||
coord=COORDINATE:NewFromVec3(vec3)
|
||||
coord.Heading = self:GetHeading() or 0
|
||||
return coord
|
||||
end
|
||||
|
||||
-- No luck try units and add Heading data
|
||||
local Units = self:GetUnits() or {}
|
||||
|
||||
for _,_unit in pairs(Units) do
|
||||
local FirstUnit = _unit -- Wrapper.Unit#UNIT
|
||||
@@ -1231,15 +1245,15 @@ function GROUP:GetCoordinate()
|
||||
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
|
||||
|
||||
if FirstUnitCoordinate then
|
||||
local Heading = self:GetHeading()
|
||||
local Heading = self:GetHeading() or 0
|
||||
FirstUnitCoordinate.Heading = Heading
|
||||
return FirstUnitCoordinate
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
-- no luck, try the API way
|
||||
|
||||
-- no luck, try the API way
|
||||
local DCSGroup = Group.getByName(self.GroupName)
|
||||
if DCSGroup then
|
||||
local DCSUnits = DCSGroup:getUnits() or {}
|
||||
@@ -1250,14 +1264,19 @@ function GROUP:GetCoordinate()
|
||||
if point then
|
||||
--self:I(point)
|
||||
local coord = COORDINATE:NewFromVec3(point)
|
||||
coord.Heading = 0
|
||||
local munit = UNIT:Find(_unit)
|
||||
if munit then
|
||||
coord.Heading = munit:GetHeading() or 0
|
||||
end
|
||||
return coord
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -2074,7 +2093,7 @@ function GROUP:Respawn( Template, Reset )
|
||||
GroupUnitVec3 = Zone:GetRandomVec3()
|
||||
else
|
||||
if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then
|
||||
GroupUnitVec3 = POINT_VEC3:NewFromVec2( From ):GetRandomPointVec3InRadius( self.InitRespawnRandomizePositionsOuter, self.InitRespawnRandomizePositionsInner )
|
||||
GroupUnitVec3 = COORDINATE:NewFromVec3(From):GetRandomVec3InRadius(self.InitRespawnRandomizePositionsOuter, self.InitRespawnRandomizePositionsInner)
|
||||
else
|
||||
GroupUnitVec3 = Zone:GetVec3()
|
||||
end
|
||||
@@ -2125,7 +2144,7 @@ function GROUP:Respawn( Template, Reset )
|
||||
GroupUnitVec3 = Zone:GetRandomVec3()
|
||||
else
|
||||
if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then
|
||||
GroupUnitVec3 = POINT_VEC3:NewFromVec2( From ):GetRandomPointVec3InRadius( self.InitRespawnRandomizePositionsOuter, self.InitRespawnRandomizePositionsInner )
|
||||
GroupUnitVec3 = COORDINATE:NewFromVec2( From ):GetRandomPointVec3InRadius( self.InitRespawnRandomizePositionsOuter, self.InitRespawnRandomizePositionsInner )
|
||||
else
|
||||
GroupUnitVec3 = Zone:GetVec3()
|
||||
end
|
||||
@@ -2797,7 +2816,7 @@ do -- Event Handling
|
||||
|
||||
self:EventDispatcher():Reset( self )
|
||||
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
for UnitID, UnitData in pairs( self:GetUnits() or {}) do
|
||||
UnitData:ResetEvents()
|
||||
end
|
||||
|
||||
@@ -3003,7 +3022,7 @@ end
|
||||
-- local callsign = mygroup:GetCustomCallSign(true,false,nil,function(groupname,playername) return string.match(playername,"([%a]+)$") end)
|
||||
--
|
||||
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations,CustomFunction,...)
|
||||
--self:I("GetCustomCallSign")
|
||||
self:T("GetCustomCallSign")
|
||||
|
||||
local callsign = "Ghost 1"
|
||||
if self:IsAlive() then
|
||||
@@ -3016,8 +3035,12 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations,C
|
||||
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
||||
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
||||
local personalized = false
|
||||
local playername = IsPlayer == true and self:GetPlayerName() or shortcallsign
|
||||
--local playername = IsPlayer == true and self:GetPlayerName() or shortcallsign
|
||||
local playername = shortcallsign
|
||||
|
||||
if IsPlayer then playername = self:GetPlayerName() end
|
||||
|
||||
self:T2("GetCustomCallSign outcome = "..playername)
|
||||
if CustomFunction and IsPlayer then
|
||||
local arguments = arg or {}
|
||||
local callsign = CustomFunction(groupname,playername,unpack(arguments))
|
||||
@@ -3137,7 +3160,7 @@ function GROUP:IsSAM()
|
||||
local units = self:GetUnits()
|
||||
for _,_unit in pairs(units or {}) do
|
||||
local unit = _unit -- Wrapper.Unit#UNIT
|
||||
if unit:HasSEAD() and unit:IsGround() and (not unit:HasAttribute("Mobile AAA")) then
|
||||
if unit:IsSAM() then
|
||||
issam = true
|
||||
break
|
||||
end
|
||||
@@ -3147,18 +3170,16 @@ end
|
||||
|
||||
--- [GROUND] Determine if a GROUP has a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute.
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean IsSAM True if AAA, else false
|
||||
-- @return #boolean IsAAA True if AAA, else false
|
||||
function GROUP:IsAAA()
|
||||
local issam = false
|
||||
local isAAA = false
|
||||
local units = self:GetUnits()
|
||||
for _,_unit in pairs(units or {}) do
|
||||
local unit = _unit -- Wrapper.Unit#UNIT
|
||||
local desc = unit:GetDesc() or {}
|
||||
local attr = desc.attributes or {}
|
||||
if unit:HasSEAD() then return false end
|
||||
if attr["AAA"] or attr["SAM related"] then
|
||||
issam = true
|
||||
if unit:IsAAA() then
|
||||
isAAA = true
|
||||
break
|
||||
end
|
||||
end
|
||||
return issam
|
||||
return isAAA
|
||||
end
|
||||
|
||||
@@ -207,10 +207,10 @@ function NET:_EventHandler(EventData)
|
||||
local PlayerID = self:GetPlayerIDByName(name) or "none"
|
||||
local PlayerSide, PlayerSlot = self:GetSlot(data.IniUnit)
|
||||
if not PlayerSide then PlayerSide = EventData.IniCoalition end
|
||||
if not PlayerSlot then PlayerSlot = EventData.IniUnit:GetID() end
|
||||
if not PlayerSlot then PlayerSlot = EventData.IniUnit:GetID() or -1 end
|
||||
local TNow = timer.getTime()
|
||||
|
||||
self:T(self.lid.."Event for: "..name.." | UCID: "..ucid .. " | ID/SIDE/SLOT "..PlayerID.."/"..PlayerSide.."/"..PlayerSlot)
|
||||
--self:T(self.lid.."Event for: "..name.." | UCID: "..ucid .. " | ID/SIDE/SLOT "..PlayerID.."/"..PlayerSide.."/"..PlayerSlot)
|
||||
|
||||
-- Joining
|
||||
if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
--- @type POSITIONABLE
|
||||
-- @field Core.Point#COORDINATE coordinate Coordinate object.
|
||||
-- @field Core.Point#POINT_VEC3 pointvec3 Point Vec3 object.
|
||||
-- @field Core.Point#COORDINATE pointvec3 Point Vec3 object.
|
||||
-- @extends Wrapper.Identifiable#IDENTIFIABLE
|
||||
|
||||
|
||||
@@ -110,14 +110,17 @@ function POSITIONABLE:Destroy( GenerateEvent )
|
||||
|
||||
if GenerateEvent and GenerateEvent == true then
|
||||
if self:IsAir() then
|
||||
--self:ScheduleOnce(1,self.CreateEventCrash,self,timer.getTime(),DCSObject)
|
||||
self:CreateEventCrash( timer.getTime(), DCSObject )
|
||||
else
|
||||
--self:ScheduleOnce(1,self.CreateEventDead,self,timer.getTime(),DCSObject)
|
||||
self:CreateEventDead( timer.getTime(), DCSObject )
|
||||
end
|
||||
elseif GenerateEvent == false then
|
||||
-- Do nothing!
|
||||
else
|
||||
self:CreateEventRemoveUnit( timer.getTime(), DCSObject )
|
||||
--self:ScheduleOnce(1,self.CreateEventRemoveUnit,self,timer.getTime(),DCSObject)
|
||||
end
|
||||
|
||||
USERFLAG:New( UnitGroupName ):Set( 100 )
|
||||
@@ -142,7 +145,11 @@ function POSITIONABLE:GetPosition()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
|
||||
if self:IsInstanceOf("GROUP") then
|
||||
DCSPositionable = self:GetFirstUnitAlive():GetDCSObject()
|
||||
end
|
||||
|
||||
if DCSPositionable then
|
||||
local PositionablePosition = DCSPositionable:getPosition()
|
||||
self:T3( PositionablePosition )
|
||||
@@ -277,9 +284,9 @@ function POSITIONABLE:GetVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a POINT_VEC2 object indicating the point in 2D of the POSITIONABLE within the mission.
|
||||
--- Returns a COORDINATE object indicating the point in 2D of the POSITIONABLE within the mission.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Point#POINT_VEC2 The 2D point vector of the POSITIONABLE.
|
||||
-- @return Core.Point#COORDINATE The 3D point vector of the POSITIONABLE.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function POSITIONABLE:GetPointVec2()
|
||||
self:F2( self.PositionableName )
|
||||
@@ -289,20 +296,20 @@ function POSITIONABLE:GetPointVec2()
|
||||
if DCSPositionable then
|
||||
local PositionableVec3 = DCSPositionable:getPosition().p
|
||||
|
||||
local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 )
|
||||
local PositionablePointVec2 = COORDINATE:NewFromVec3( PositionableVec3 )
|
||||
|
||||
-- self:F( PositionablePointVec2 )
|
||||
return PositionablePointVec2
|
||||
end
|
||||
|
||||
self:E( { "Cannot GetPointVec2", Positionable = self, Alive = self:IsAlive() } )
|
||||
self:E( { "Cannot Coordinate", Positionable = self, Alive = self:IsAlive() } )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a POINT_VEC3 object indicating the point in 3D of the POSITIONABLE within the mission.
|
||||
--- Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Point#POINT_VEC3 The 3D point vector of the POSITIONABLE.
|
||||
-- @return Core.Point#COORDINATE The 3D point vector of the POSITIONABLE.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function POSITIONABLE:GetPointVec3()
|
||||
|
||||
@@ -322,8 +329,8 @@ function POSITIONABLE:GetPointVec3()
|
||||
|
||||
else
|
||||
|
||||
-- Create a new POINT_VEC3 object.
|
||||
self.pointvec3 = POINT_VEC3:NewFromVec3( PositionableVec3 )
|
||||
-- Create a new COORDINATE object.
|
||||
self.pointvec3 = COORDINATE:NewFromVec3( PositionableVec3 )
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ function SCENERY:Register( SceneryName, SceneryObject )
|
||||
|
||||
self.SceneryObject = SceneryObject
|
||||
|
||||
if self.SceneryObject then
|
||||
self.Life0 = self.SceneryObject:getLife()
|
||||
if self.SceneryObject and self.SceneryObject.getLife then -- fix some objects do not have all functions
|
||||
self.Life0 = self.SceneryObject:getLife() or 0
|
||||
else
|
||||
self.Life0 = 0
|
||||
end
|
||||
@@ -59,7 +59,7 @@ function SCENERY:Register( SceneryName, SceneryObject )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
||||
--- Returns the value of the scenery with the given PropertyName, or nil if no matching property exists.
|
||||
-- @param #SCENERY self
|
||||
-- @param #string PropertyName The name of a the QuadZone Property from the scenery assignment to be retrieved.
|
||||
-- @return #string The Value of the QuadZone Property from the scenery assignment with the given PropertyName, or nil if absent.
|
||||
@@ -67,6 +67,14 @@ function SCENERY:GetProperty(PropertyName)
|
||||
return self.Properties[PropertyName]
|
||||
end
|
||||
|
||||
--- Checks if the value of the scenery with the given PropertyName exists.
|
||||
-- @param #SCENERY self
|
||||
-- @param #string PropertyName The name of a the QuadZone Property from the scenery assignment to be retrieved.
|
||||
-- @return #boolean Outcome True if it exists, else false.
|
||||
function SCENERY:HasProperty(PropertyName)
|
||||
return self.Properties[PropertyName] ~= nil and true or false
|
||||
end
|
||||
|
||||
--- Returns the scenery Properties table.
|
||||
-- @param #SCENERY self
|
||||
-- @return #table The Key:Value table of QuadZone properties of the zone from the scenery assignment .
|
||||
@@ -83,6 +91,7 @@ function SCENERY:SetProperty(PropertyName, PropertyValue)
|
||||
self.Properties[PropertyName] = PropertyValue
|
||||
return self
|
||||
end
|
||||
|
||||
--- Obtain object name.
|
||||
--@param #SCENERY self
|
||||
--@return #string Name
|
||||
@@ -97,7 +106,7 @@ function SCENERY:GetDCSObject()
|
||||
return self.SceneryObject
|
||||
end
|
||||
|
||||
--- Get current life points from the SCENERY Object.
|
||||
--- Get current life points from the SCENERY Object. Note - Some scenery objects always have 0 life points.
|
||||
-- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the life0 value to 120%
|
||||
-- of the last life value if life exceeds life0 (initial life) at any point. Thus will will get a smooth percentage decrease, if you use this e.g. as success
|
||||
-- criteria for a bombing task.
|
||||
@@ -105,7 +114,7 @@ end
|
||||
--@return #number life
|
||||
function SCENERY:GetLife()
|
||||
local life = 0
|
||||
if self.SceneryObject then
|
||||
if self.SceneryObject and self.SceneryObject.getLife then
|
||||
life = self.SceneryObject:getLife()
|
||||
if life > self.Life0 then
|
||||
self.Life0 = math.floor(life * 1.2)
|
||||
@@ -121,7 +130,7 @@ function SCENERY:GetLife0()
|
||||
return self.Life0 or 0
|
||||
end
|
||||
|
||||
--- Check if SCENERY Object is alive.
|
||||
--- Check if SCENERY Object is alive. Note - Some scenery objects always have 0 life points.
|
||||
--@param #SCENERY self
|
||||
--@param #number Threshold (Optional) If given, SCENERY counts as alive above this relative life in percent (1..100).
|
||||
--@return #number life
|
||||
@@ -133,7 +142,7 @@ function SCENERY:IsAlive(Threshold)
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if SCENERY Object is dead.
|
||||
--- Check if SCENERY Object is dead. Note - Some scenery objects always have 0 life points.
|
||||
--@param #SCENERY self
|
||||
--@param #number Threshold (Optional) If given, SCENERY counts as dead below this relative life in percent (1..100).
|
||||
--@return #number life
|
||||
@@ -145,12 +154,13 @@ function SCENERY:IsDead(Threshold)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get SCENERY relative life in percent, e.g. 75.
|
||||
--- Get SCENERY relative life in percent, e.g. 75. Note - Some scenery objects always have 0 life points.
|
||||
--@param #SCENERY self
|
||||
--@return #number rlife
|
||||
function SCENERY:GetRelativeLife()
|
||||
local life = self:GetLife()
|
||||
local life0 = self:GetLife0()
|
||||
if life == 0 or life0 == 0 then return 0 end
|
||||
local rlife = math.floor((life/life0)*100)
|
||||
return rlife
|
||||
end
|
||||
|
||||
@@ -203,7 +203,7 @@ STORAGE.Type = {
|
||||
|
||||
--- STORAGE class version.
|
||||
-- @field #string version
|
||||
STORAGE.version="0.1.4"
|
||||
STORAGE.version="0.1.5"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -227,11 +227,11 @@ function STORAGE:New(AirbaseName)
|
||||
|
||||
self.airbase=Airbase.getByName(AirbaseName)
|
||||
|
||||
if Airbase.getWarehouse then
|
||||
if Airbase.getWarehouse and self.airbase then
|
||||
self.warehouse=self.airbase:getWarehouse()
|
||||
end
|
||||
|
||||
self.lid = string.format("STORAGE %s", AirbaseName)
|
||||
self.lid = string.format("STORAGE %s | ", AirbaseName)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -251,7 +251,7 @@ function STORAGE:NewFromStaticCargo(StaticCargoName)
|
||||
self.warehouse=Warehouse.getCargoAsWarehouse(self.airbase)
|
||||
end
|
||||
|
||||
self.lid = string.format("STORAGE %s", StaticCargoName)
|
||||
self.lid = string.format("STORAGE %s | ", StaticCargoName)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -271,7 +271,7 @@ function STORAGE:NewFromDynamicCargo(DynamicCargoName)
|
||||
self.warehouse=Warehouse.getCargoAsWarehouse(self.airbase)
|
||||
end
|
||||
|
||||
self.lid = string.format("STORAGE %s", DynamicCargoName)
|
||||
self.lid = string.format("STORAGE %s | ", DynamicCargoName)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -656,7 +656,6 @@ function STORAGE:SaveToFile(Path,Filename)
|
||||
for key,amount in pairs(lq) do
|
||||
DataLiquids = DataLiquids..tostring(key).."="..tostring(amount).."\n"
|
||||
end
|
||||
--self:I(DataLiquids)
|
||||
UTILS.SaveToFile(Path,Filename.."_Liquids.csv",DataLiquids)
|
||||
if self.verbose and self.verbose > 0 then
|
||||
self:I(self.lid.."Saving Liquids to "..tostring(Path).."\\"..tostring(Filename).."_Liquids.csv")
|
||||
@@ -668,7 +667,6 @@ function STORAGE:SaveToFile(Path,Filename)
|
||||
for key,amount in pairs(ac) do
|
||||
DataAircraft = DataAircraft..tostring(key).."="..tostring(amount).."\n"
|
||||
end
|
||||
--self:I(DataAircraft)
|
||||
UTILS.SaveToFile(Path,Filename.."_Aircraft.csv",DataAircraft)
|
||||
if self.verbose and self.verbose > 0 then
|
||||
self:I(self.lid.."Saving Aircraft to "..tostring(Path).."\\"..tostring(Filename).."_Aircraft.csv")
|
||||
@@ -677,9 +675,17 @@ function STORAGE:SaveToFile(Path,Filename)
|
||||
|
||||
if UTILS.TableLength(wp) > 0 then
|
||||
DataWeapons = DataWeapons .."Weapons and Materiel in Storage:\n"
|
||||
for key,amount in pairs(wp) do
|
||||
DataWeapons = DataWeapons..tostring(key).."="..tostring(amount).."\n"
|
||||
|
||||
for _,_category in pairs(ENUMS.Storage.weapons) do
|
||||
for _,_key in pairs(_category) do
|
||||
local amount = self:GetAmount(_key)
|
||||
if type(_key) == "table" then
|
||||
_key = "{"..table.concat(_key,",").."}"
|
||||
end
|
||||
DataWeapons = DataWeapons..tostring(_key).."="..tostring(amount).."\n"
|
||||
end
|
||||
end
|
||||
|
||||
-- Gazelle table keys
|
||||
for key,amount in pairs(ENUMS.Storage.weapons.Gazelle) do
|
||||
amount = self:GetItemAmount(ENUMS.Storage.weapons.Gazelle[key])
|
||||
@@ -705,7 +711,6 @@ function STORAGE:SaveToFile(Path,Filename)
|
||||
amount = self:GetItemAmount(ENUMS.Storage.weapons.AH64D[key])
|
||||
DataWeapons = DataWeapons.."ENUMS.Storage.weapons.AH64D."..tostring(key).."="..tostring(amount).."\n"
|
||||
end
|
||||
--self:I(DataAircraft)
|
||||
UTILS.SaveToFile(Path,Filename.."_Weapons.csv",DataWeapons)
|
||||
if self.verbose and self.verbose > 0 then
|
||||
self:I(self.lid.."Saving Weapons to "..tostring(Path).."\\"..tostring(Filename).."_Weapons.csv")
|
||||
@@ -777,14 +782,26 @@ function STORAGE:LoadFromFile(Path,Filename)
|
||||
local Ok,Weapons = UTILS.LoadFromFile(Path,Filename.."_Weapons.csv")
|
||||
if Ok then
|
||||
if self.verbose and self.verbose > 0 then
|
||||
self:I(self.lid.."Loading _eapons from "..tostring(Path).."\\"..tostring(Filename).."_Weapons.csv")
|
||||
self:I(self.lid.."Loading Weapons from "..tostring(Path).."\\"..tostring(Filename).."_Weapons.csv")
|
||||
end
|
||||
for _id,_line in pairs(Weapons) do
|
||||
if string.find(_line,"Storage") == nil then
|
||||
local tbl=UTILS.Split(_line,"=")
|
||||
local wpname = tbl[1]
|
||||
local wpnumber = tonumber(tbl[2])
|
||||
self:SetAmount(wpname,wpnumber)
|
||||
if string.find(wpname,"{") == 1 then
|
||||
--self:I("Found a table: "..wpname)
|
||||
wpname = string.gsub(wpname,"{","")
|
||||
wpname = string.gsub(wpname,"}","")
|
||||
local tbl = UTILS.Split(wpname,",")
|
||||
local wptbl = {}
|
||||
for _id,_key in ipairs(tbl) do
|
||||
table.insert(wptbl,_id,_key)
|
||||
end
|
||||
self:SetAmount(wptbl,wpnumber)
|
||||
else
|
||||
self:SetAmount(wpname,wpnumber)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -823,6 +840,35 @@ function STORAGE:StopAutoSave()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Try to find the #STORAGE object of one of the many "H"-Helipads in Syria. You need to put a (small, round) zone on top of it, because the name is not unique(!).
|
||||
-- @param #STORAGE self
|
||||
-- @param #string ZoneName The name of the zone where to find the helipad.
|
||||
-- @return #STORAGE self or nil if not found.
|
||||
function STORAGE:FindSyriaHHelipadWarehouse(ZoneName)
|
||||
local findzone = ZONE:New(ZoneName)
|
||||
local base = world.getAirbases()
|
||||
for i = 1, #base do
|
||||
local info = {}
|
||||
--info.desc = Airbase.getDesc(base[i])
|
||||
info.callsign = Airbase.getCallsign(base[i])
|
||||
info.id = Airbase.getID(base[i])
|
||||
--info.cat = Airbase.getCategory(base[i])
|
||||
info.point = Airbase.getPoint(base[i])
|
||||
info.coordinate = COORDINATE:NewFromVec3(info.point)
|
||||
info.DCSObject = base[i]
|
||||
--if Airbase.getUnit(base[i]) then
|
||||
--info.unitId = Airbase.getUnit(base[i]):getID()
|
||||
--end
|
||||
if info.callsign == "H" and findzone:IsCoordinateInZone(info.coordinate) then
|
||||
info.warehouse = info.DCSObject:getWarehouse()
|
||||
info.Storage = STORAGE:New(info.callsign..info.id)
|
||||
info.Storage.airbase = info.DCSObject
|
||||
info.Storage.warehouse = info.warehouse
|
||||
return info.Storage
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user