Compare commits

...

14 Commits
2.7.5 ... 2.7.6

Author SHA1 Message Date
Applevangelist
6cc3d73c04 Changed priority to show bomb target height in ft if no Player Settings 2021-08-22 12:02:52 +02:00
Applevangelist
e541e39403 Bug fixes. Added support for Ships as load zones 2021-08-22 12:02:26 +02:00
Applevangelist
c7ea45e5fd Clean up UTF-8 mess 2021-08-18 18:01:04 +02:00
Applevangelist
20f28b3d2c Fix for SAM pattern matching not working 2021-08-18 15:01:14 +02:00
Applevangelist
f3f63ab8aa Fix for degree sign extra char 2021-08-18 11:52:13 +02:00
Applevangelist
e91090cfff more corrections 2021-08-18 11:47:25 +02:00
Applevangelist
1a7fb3c13e Fix for degree sign extra char 2021-08-18 11:36:33 +02:00
Applevangelist
59857ed79d CSAR - added changes from Development for AFB landings and safer calculation of distances of lost pilots 2021-08-18 09:29:58 +02:00
Frank
4797665939 Update Range.lua
- Added demo by shagrat
2021-08-02 22:02:21 +02:00
Applevangelist
b89749036d Merge pull request #1584 from FlightControl-Master/Applevangelist-patch-3-1
Update Utils.lua
2021-08-02 19:04:57 +02:00
Applevangelist
c6268488de Update Utils.lua 2021-08-02 19:03:07 +02:00
Applevangelist
de04369703 Various for AI_CARGO, CTLD, CSAR, align with dev 2021-07-29 12:46:16 +02:00
Applevangelist
05b6f19a87 Merge pull request #1583 from FlightControl-Master/Applevangelist-patch-2
Update CSAR.lua
2021-07-28 19:47:48 +02:00
Applevangelist
2753df8216 Update CSAR.lua
Fix for CSAR message to all not working, added option to suppress all messaging, make destroys silent to not affect scoring
2021-07-28 19:45:47 +02:00
13 changed files with 323 additions and 201 deletions

View File

@@ -47,19 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
self:SetStartState( "Unloaded" )
self:AddTransition( "Unloaded", "Pickup", "*" )
self:AddTransition( "Loaded", "Deploy", "*" )
-- Board
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Reload", "*" )
self:AddTransition( "*", "Board", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
self:AddTransition( "*", "Load", "Boarding" )
self:AddTransition( "Boarding", "Board", "Boarding" )
self:AddTransition( "Loaded", "Board", "Loaded" )
self:AddTransition( "Boarding", "Loaded", "Boarding" )
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
-- Unload
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "Unboard", "*" )
self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
self:AddTransition( "Loaded", "Unload", "Unboarding" )
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
--- Pickup Handler OnBefore for AI_CARGO
-- @function [parent=#AI_CARGO] OnBeforePickup
@@ -393,7 +395,7 @@ end
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } )
if Carrier and Carrier:IsAlive() and From == "Boarding" then
if Carrier and Carrier:IsAlive() then
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
@@ -509,7 +511,7 @@ end
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
if Carrier and Carrier:IsAlive() and From == "Unboarding" then
if Carrier and Carrier:IsAlive() then
if not Cargo:IsUnLoaded() then
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
return
@@ -580,4 +582,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend
end
end

View File

@@ -98,7 +98,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
self:AddTransition( "*", "Guard", "Unloaded" )
self:AddTransition( "*", "Home", "*" )
self:AddTransition( "*", "Reload", "Boarding" )
self:AddTransition( "*", "Deployed", "*" )
self:AddTransition( "*", "PickedUp", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
self:SetCombatRadius( CombatRadius )

View File

@@ -64,21 +64,25 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
self:SetStartState( "Unloaded" )
-- Boarding
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
self:AddTransition( "Unloaded", "Pickup", "*" )
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
self:AddTransition( "Unloaded", "Unboard", "Unloaded" )
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" )
self:AddTransition( "*", "PickedUp", "*" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" )
-- Unboarding
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" )
self:AddTransition( "*", "Destroyed", "*" )
self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
-- RTB
self:AddTransition( "*", "Home" , "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
-- @param #AI_CARGO_HELICOPTER self
@@ -207,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self:SetCarrier( Helicopter )
self.landingspeed = 15 -- kph
self.landingheight = 5.5 -- meter
return self
end
@@ -255,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
return self
end
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
-- @param #AI_CARGO_HELICOPTER self
-- @param #number speed Landing speed in kph(!), e.g. 15
-- @param #number height Landing height in meters(!), e.g. 5.5
-- @return #AI_CARGO_HELICOPTER self
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
--
-- BASE:TraceOn()
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
--
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
-- Adjust if necessary.
function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height)
local _speed = speed or 15
local _height = height or 5.5
self.landingheight = _height
self.landingspeed = _speed
return self
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
@@ -271,13 +297,13 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
-- 1 - When the helo lands normally on the ground.
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
-- For point 2, this is an issue, the infantry may not unload in this case!
-- So we check if the helo is on the ground, and velocity< 5.
-- So we check if the helo is on the ground, and velocity< 15.
-- Only then the infantry can unload (and load too, for consistency)!
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
if self.RoutePickup == true then
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
--self:Load( Helicopter:GetPointVec2() )
self:Load( self.PickupZone )
self.RoutePickup = false
@@ -285,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
end
if self.RouteDeploy == true then
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
self:Unload( self.DeployZone )
self.RouteDeploy = false
end

View File

@@ -1707,8 +1707,8 @@ end
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
-- @param #FOX self
-- @param DCS#Weapon weapon The weapon.
-- @return #number Notching heading right, i.e. missile heading +90<EFBFBD>
-- @return #number Notching heading left, i.e. missile heading -90<EFBFBD>.
-- @return #number Notching heading right, i.e. missile heading +90°.
-- @return #number Notching heading left, i.e. missile heading -90°.
function FOX:_GetNotchingHeadings(weapon)
if weapon then

View File

@@ -5,7 +5,7 @@
-- ## Features:
--
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <EFBFBD>
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range °
-- * Provide alerts when a missile would have killed your aircraft.
-- * Provide alerts when the missile self destructs.
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.

View File

@@ -5371,7 +5371,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
if spawnonground then
-- Sh<EFBFBD>ps and FARPS seem to have a build in queue.
-- Sh°ps and FARPS seem to have a build in queue.
if spawnonship or spawnonfarp or spawnonrunway or automatic then
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))

View File

@@ -32,7 +32,9 @@
--
-- ===
--
-- ## Missions: Example missions will be added later.
-- ## Missions:
--
-- * [MAR - On the Range - MOOSE - SC](https://www.digitalcombatsimulator.com/en/files/3317765/) by shagrat
--
-- ===
--
@@ -2558,7 +2560,7 @@ function RANGE:_DisplayBombTargets(_unitname)
-- Get elevation
local elevation=coord:GetLandHeight()
local eltxt=string.format("%d m", elevation)
if _settings:IsImperial() then
if not _settings:IsMetric() then
elevation=UTILS.MetersToFeet(elevation)
eltxt=string.format("%d ft", elevation)
end

View File

@@ -92,7 +92,7 @@ function SEAD:New( SEADGroupPrefixes )
end
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.2.8")
self:I("*** SEAD - Started Version 0.2.9")
return self
end
@@ -102,7 +102,7 @@ end
-- @return #SEAD self
function SEAD:UpdateSet( SEADGroupPrefixes )
self:F( SEADGroupPrefixes )
self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
@@ -120,7 +120,7 @@ end
-- @param #number range Set the engagement range in percent, e.g. 50
-- @return self
function SEAD:SetEngagementRange(range)
self:F( { range } )
self:T( { range } )
range = range or 75
if range < 0 or range > 100 then
range = 75
@@ -135,7 +135,7 @@ end
-- @param #string WeaponName
-- @return #boolean Returns true for a match
function SEAD:_CheckHarms(WeaponName)
self:F( { WeaponName } )
self:T( { WeaponName } )
local hit = false
for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end
@@ -166,7 +166,7 @@ function SEAD:HandleEventShot( EventData )
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
if _targetUnit and _targetUnit:IsAlive() then
local _targetMimgroup = _targetUnit:GetGroup()
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
_targetMimgroupName = _targetMimgroup:GetName() -- group name
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
self:T( self.SEADGroupPrefixes )
self:T( _targetMimgroupName )
@@ -174,6 +174,7 @@ function SEAD:HandleEventShot( EventData )
-- see if we are shot at
local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( SEADGroupPrefix )
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
SEADGroupFound = true
self:T( '*** SEAD - Group Found' )

View File

@@ -725,7 +725,7 @@
--
-- The same holds true after the recovery window closes. The carrier will head back to the place where he left its assigned route and resume the path to the next waypoint defined in the mission editor.
--
-- Note that the carrier will only head into the wind, if the wind direction is different by more than 5° from the current heading of the carrier (the angled runway, if any, fis taken into account here).
-- Note that the carrier will only head into the wind, if the wind direction is different by more than 5° from the current heading of the carrier (the angled runway, if any, fis taken into account here).
--
-- ===
--
@@ -864,10 +864,10 @@
--
-- The graph displays the lineup error (LUE) as a function of the distance to the carrier.
--
-- The pilot approaches the carrier from the port side, LUE>0°, at a distance of ~1 NM.
-- At the beginning of the groove (X), he significantly overshoots to the starboard side (LUE<5°).
-- The pilot approaches the carrier from the port side, LUE>0°, at a distance of ~1 NM.
-- At the beginning of the groove (X), he significantly overshoots to the starboard side (LUE<5°).
-- In the middle (IM), he performs good corrections and smoothly reduces the lineup error.
-- Finally, at a distance of ~0.3 NM (IC) he has corrected his lineup with the runway to a reasonable level, |LUE|<0.5°.
-- Finally, at a distance of ~0.3 NM (IC) he has corrected his lineup with the runway to a reasonable level, |LUE|<0.5°.
--
-- ## Glideslope Error
--
@@ -876,7 +876,7 @@
-- The graph displays the glideslope error (GSE) as a function of the distance to the carrier.
--
-- In this case the pilot already enters the groove (X) below the optimal glideslope. He is not able to correct his height in the IM part and
-- stays significantly too low. In close, he performs a harsh correction to gain altitude and ends up even slightly too high (GSE>0.5°).
-- stays significantly too low. In close, he performs a harsh correction to gain altitude and ends up even slightly too high (GSE>0.5°).
-- At his point further corrections are necessary.
--
-- ## Angle of Attack
@@ -2438,7 +2438,7 @@ end
-- @param #number duration Default duration of the recovery in minutes. Default 30 min.
-- @param #number windondeck Default wind on deck in knots. Default 25 knots.
-- @param #boolean uturn U-turn after recovery window closes on=true or off=false/nil. Default off.
-- @param #number offset Relative Marshal radial in degrees for Case II/III recoveries. Default 30°.
-- @param #number offset Relative Marshal radial in degrees for Case II/III recoveries. Default 30°.
-- @return #AIRBOSS self
function AIRBOSS:SetMenuRecovery(duration, windondeck, uturn, offset)
@@ -3878,7 +3878,7 @@ function AIRBOSS:_CheckRecoveryTimes()
-- Check if time is less than 5 minutes.
if nextwindow.WIND and nextwindow.START-time<self.dTturn and not self.turnintowind then
-- Check that wind is blowing from a direction > 5° different from the current heading.
-- Check that wind is blowing from a direction > 5° different from the current heading.
local hdg=self:GetHeading()
local wind=self:GetHeadingIntoWind()
local delta=self:_GetDeltaHeading(hdg, wind)
@@ -3896,7 +3896,7 @@ function AIRBOSS:_CheckRecoveryTimes()
end
--Debug info
self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s", hdg, wind,UTILS.MpsToKnots(vwind), delta, tostring(uturn)))
self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s", hdg, wind,UTILS.MpsToKnots(vwind), delta, tostring(uturn)))
-- Time into the wind 1 day or if longer recovery time + the 5 min early.
local t=math.max(nextwindow.STOP-nextwindow.START+self.dTturn, 60*60*24)
@@ -6933,7 +6933,7 @@ function AIRBOSS:_AddMarshalGroup(flight, stack)
-- For case 1 we want the BRC but above routine return FB.
radial=self:GetBRC()
end
local text=string.format("Select TACAN %03d°, channel %d%s (%s)", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
local text=string.format("Select TACAN %03d°, channel %d%s (%s)", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
self:MessageToPlayer(flight, text, nil, "")
end
@@ -9346,7 +9346,7 @@ function AIRBOSS:_Initial(playerData)
-- Hook down for students.
if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then
if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
hint=hint.." - Hook down, SAS on, Wing Sweep 68°!"
hint=hint.." - Hook down, SAS on, Wing Sweep 68°!"
else
hint=hint.." - Hook down!"
end
@@ -11331,15 +11331,15 @@ function AIRBOSS:_AttitudeMonitor(playerData)
-- Output
local text=string.format("Pattern step: %s", step)
text=text..string.format("\nAoA=%.1f° = %.1f Units | |V|=%.1f knots", aoa, self:_AoADeg2Units(playerData, aoa), UTILS.MpsToKnots(vabs))
text=text..string.format("\nAoA=%.1f° = %.1f Units | |V|=%.1f knots", aoa, self:_AoADeg2Units(playerData, aoa), UTILS.MpsToKnots(vabs))
if self.Debug then
-- Velocity vector.
text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s", velo.x, velo.y, velo.z)
--Wind vector.
text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s", wind.x, wind.y, wind.z)
end
text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw)
text=text..string.format("\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y*196.85)
text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw)
text=text..string.format("\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y*196.85)
local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit)
-- Get player velocity in km/h.
local vplayer=playerData.unit:GetVelocityKMH()
@@ -11360,14 +11360,14 @@ function AIRBOSS:_AttitudeMonitor(playerData)
playerData.step==AIRBOSS.PatternStep.GROOVE_IW then
local lue=self:_Lineup(playerData.unit, true)
local gle=self:_Glideslope(playerData.unit)
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
text=text..string.format("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units", lue, gle, self:_AoADeg2Units(playerData, aoa))
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
text=text..string.format("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units", lue, gle, self:_AoADeg2Units(playerData, aoa))
local grade, points, analysis=self:_LSOgrade(playerData)
text=text..string.format("\nTgroove=%.1f sec", self:_GetTimeInGroove(playerData))
text=text..string.format("\nGrade: %s %.1f PT - %s", grade, points, analysis)
else
text=text..string.format("\nR=%.2f NM | X=%d Z=%d m", UTILS.MetersToNM(rho), dx, dz)
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
end
MESSAGE:New(text, 1, nil , true):ToClient(playerData.client)
@@ -12695,7 +12695,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
if self.holdingoffset<0 then
turn="left"
end
hint=hint..string.format("\nTurn %s and select TACAN %03d°.", turn, radial)
hint=hint..string.format("\nTurn %s and select TACAN %03d°.", turn, radial)
end
end
@@ -12704,7 +12704,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
if playerData.step==AIRBOSS.PatternStep.DIRTYUP then
if playerData.difficulty==AIRBOSS.Difficulty.EASY then
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
hint=hint.."\nFAF! Checks completed. Nozzles 50°."
hint=hint.."\nFAF! Checks completed. Nozzles 50°."
else
--TODO: Tomcat?
hint=hint.."\nDirty up! Hook, gear and flaps down."
@@ -12773,14 +12773,14 @@ function AIRBOSS:_StepHint(playerData, step)
-- Late break.
if step==AIRBOSS.PatternStep.LATEBREAK then
if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
hint=hint.."\nWing Sweep 20°, Gear DOWN < 280 KIAS."
hint=hint.."\nWing Sweep 20°, Gear DOWN < 280 KIAS."
end
end
-- Abeam.
if step==AIRBOSS.PatternStep.ABEAM then
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
hint=hint.."\nNozzles 50°-60°. Antiskid OFF. Lights OFF."
hint=hint.."\nNozzles 50°-60°. Antiskid OFF. Lights OFF."
elseif playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
hint=hint.."\nSlats/Flaps EXTENDED < 225 KIAS. DLC SELECTED. Auto Throttle IF DESIRED."
else
@@ -13215,7 +13215,7 @@ function AIRBOSS:_Debrief(playerData)
end
-- Re-enter message.
local text=string.format("fly heading %03d° for %d NM to re-enter the pattern.", heading, UTILS.MetersToNM(distance))
local text=string.format("fly heading %03d° for %d NM to re-enter the pattern.", heading, UTILS.MetersToNM(distance))
self:MessageToPlayer(playerData, text, "LSO", nil, nil, false, 5)
else
@@ -13411,9 +13411,9 @@ function AIRBOSS:_CheckCollisionCoord(coordto, coordfrom)
local text=""
if clear then
text=string.format("Path into direction %03d° is clear for the next %.1f NM.", direction, UTILS.MetersToNM(d))
text=string.format("Path into direction %03d° is clear for the next %.1f NM.", direction, UTILS.MetersToNM(d))
else
text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.", UTILS.MetersToNM(d), direction)
text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.", UTILS.MetersToNM(d), direction)
end
self:T2(self.lid..text)
@@ -13473,7 +13473,7 @@ function AIRBOSS:_Pathfinder()
local collision=self:_CheckFreePathToNextWP(fromcoord)
-- Debug info.
self:T2(self.lid..string.format("Pathfinder d=%.1f m, direction=%03d°, collision=%s", distance, direction, tostring(collision)))
self:T2(self.lid..string.format("Pathfinder d=%.1f m, direction=%03d°, collision=%s", distance, direction, tostring(collision)))
-- If path is clear, we start a little detour.
if not collision then
@@ -13907,7 +13907,7 @@ function AIRBOSS:_CheckPatternUpdate()
-- Update if carrier moves by more than 2.5 NM.
local Dupdate=UTILS.NMToMeters(2.5)
-- Update if carrier turned by more than 5°.
-- Update if carrier turned by more than 5°.
local Hupdate=5
-----------------------
@@ -13941,7 +13941,7 @@ function AIRBOSS:_CheckPatternUpdate()
-- Check if orientation changed.
local Hchange=false
if math.abs(deltaHeading)>=Hupdate then
self:T(self.lid..string.format("Carrier heading changed by %d°.", deltaHeading))
self:T(self.lid..string.format("Carrier heading changed by %d°.", deltaHeading))
Hchange=true
end
@@ -15642,7 +15642,7 @@ end
function AIRBOSS:_MarshalCallNewFinalBearing(FB)
-- Subtitle.
local text=string.format("new final bearing %03d°.", FB)
local text=string.format("new final bearing %03d°.", FB)
-- Debug message.
self:I(self.lid..text)
@@ -15665,7 +15665,7 @@ end
function AIRBOSS:_MarshalCallCarrierTurnTo(hdg)
-- Subtitle.
local text=string.format("carrier is now starting turn to heading %03d°.", hdg)
local text=string.format("carrier is now starting turn to heading %03d°.", hdg)
-- Debug message.
self:I(self.lid..text)
@@ -15718,11 +15718,11 @@ function AIRBOSS:_MarshalCallRecoveryStart(case)
-- Debug output.
local text=string.format("Starting aircraft recovery Case %d ops.", case)
if case==1 then
text=text..string.format(" BRC %03d°.", self:GetBRC())
text=text..string.format(" BRC %03d°.", self:GetBRC())
elseif case==2 then
text=text..string.format(" Marshal radial %03d°. BRC %03d°.", radial, self:GetBRC())
text=text..string.format(" Marshal radial %03d°. BRC %03d°.", radial, self:GetBRC())
elseif case==3 then
text=text..string.format(" Marshal radial %03d°. Final heading %03d°.", radial, self:GetFinalBearing(false))
text=text..string.format(" Marshal radial %03d°. Final heading %03d°.", radial, self:GetFinalBearing(false))
end
self:T(self.lid..text)
@@ -15767,7 +15767,7 @@ function AIRBOSS:_MarshalCallArrived(modex, case, brc, altitude, charlie, qfe)
local CT=UTILS.Split(clock[1], ":")
-- Subtitle text.
local text=string.format("Case %d, expected BRC %03d°, hold at angels %d. Expected Charlie Time %s. Altimeter %.2f. Report see me.", case, brc, angels, charlie, qfe)
local text=string.format("Case %d, expected BRC %03d°, hold at angels %d. Expected Charlie Time %s. Altimeter %.2f. Report see me.", case, brc, angels, charlie, qfe)
-- Debug message.
self:I(self.lid..text)
@@ -15944,11 +15944,11 @@ function AIRBOSS:_AddF10Commands(_unitName)
missionCommands.addCommandForGroup(gid, "60 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 60)
missionCommands.addCommandForGroup(gid, "90 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 90)
local _menusetrtime=missionCommands.addSubMenuForGroup(gid, "Set Marshal Radial", _skipperPath)
missionCommands.addCommandForGroup(gid, "+30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 30)
missionCommands.addCommandForGroup(gid, "+15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 15)
missionCommands.addCommandForGroup(gid, "0°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0)
missionCommands.addCommandForGroup(gid, "-15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -15)
missionCommands.addCommandForGroup(gid, "-30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -30)
missionCommands.addCommandForGroup(gid, "+30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 30)
missionCommands.addCommandForGroup(gid, "+15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 15)
missionCommands.addCommandForGroup(gid, "", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0)
missionCommands.addCommandForGroup(gid, "-15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -15)
missionCommands.addCommandForGroup(gid, "-30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -30)
missionCommands.addCommandForGroup(gid, "U-turn On/Off", _skipperPath, self._SkipperRecoveryUturn, self, _unitName)
missionCommands.addCommandForGroup(gid, "Start CASE I", _skipperPath, self._SkipperStartRecovery, self, _unitName, 1)
missionCommands.addCommandForGroup(gid, "Start CASE II", _skipperPath, self._SkipperStartRecovery, self, _unitName, 2)
@@ -16005,7 +16005,7 @@ function AIRBOSS:_SkipperStartRecovery(_unitName, case)
-- Inform player.
local text=string.format("affirm, Case %d recovery will start in 5 min for %d min. Wind on deck %d knots. U-turn=%s.", case, self.skipperTime, self.skipperSpeed, tostring(self.skipperUturn))
if case>1 then
text=text..string.format(" Marshal radial %d°.", self.skipperOffset)
text=text..string.format(" Marshal radial %d°.", self.skipperOffset)
end
if self:IsRecovering() then
text="negative, carrier is already recovering."
@@ -16071,7 +16071,7 @@ function AIRBOSS:_SkipperRecoveryOffset(_unitName, offset)
if playerData then
-- Inform player.
local text=string.format("roger, relative CASE II/III Marshal radial set to %d°.", offset)
local text=string.format("roger, relative CASE II/III Marshal radial set to %d°.", offset)
self:MessageToPlayer(playerData, text, "AIRBOSS")
self.skipperOffset=offset
@@ -16534,7 +16534,7 @@ function AIRBOSS:_RequestCommence(_unitName)
-- For case 1 we want the BRC but above routine return FB.
radial=self:GetBRC()
end
text=text..string.format("\nSelect TACAN %03d°, Channel %d%s (%s).\n", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
text=text..string.format("\nSelect TACAN %03d°, Channel %d%s (%s).\n", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
end
-- TODO: Inform section members.
@@ -17146,7 +17146,7 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
-- Only include current and future recovery windows.
if Tabs<recovery.STOP then
-- Output text.
recoverytext=recoverytext..string.format("\n* %s - %s: Case %d (%d°)", UTILS.SecondsToClock(recovery.START), UTILS.SecondsToClock(recovery.STOP), recovery.CASE, recovery.OFFSET)
recoverytext=recoverytext..string.format("\n* %s - %s: Case %d (%d°)", UTILS.SecondsToClock(recovery.START), UTILS.SecondsToClock(recovery.STOP), recovery.CASE, recovery.OFFSET)
if recovery.WIND then
recoverytext=recoverytext..string.format(" @ %.1f kts wind", recovery.SPEED)
end
@@ -17187,9 +17187,9 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
text=text..string.format("Case %d recovery ops\n", self.case)
else
local radial=self:GetRadial(self.case, true, true, false)
text=text..string.format("Case %d recovery ops\nMarshal radial %03d°\n", self.case, radial)
text=text..string.format("Case %d recovery ops\nMarshal radial %03d°\n", self.case, radial)
end
text=text..string.format("BRC %03d° - FB %03d°\n", self:GetBRC(), self:GetFinalBearing(true))
text=text..string.format("BRC %03d° - FB %03d°\n", self:GetBRC(), self:GetFinalBearing(true))
text=text..string.format("Speed %.1f kts - Wind on deck %.1f kts\n", carrierspeed, wind)
text=text..string.format("Tower frequency %.3f MHz\n", self.TowerFreq)
text=text..string.format("Marshal radio %.3f MHz\n", self.MarshalFreq)
@@ -17250,10 +17250,10 @@ function AIRBOSS:_DisplayCarrierWeather(_unitname)
local WodPA=UTILS.MpsToKnots(WodPA)
local WodPP=UTILS.MpsToKnots(WodPP)
local WD=string.format('%03d°', Wd)
local Ts=string.format("%d°C",T)
local WD=string.format('%03d°', Wd)
local Ts=string.format("%d°C",T)
local tT=string.format("%d°C",T)
local tT=string.format("%d°C",T)
local tW=string.format("%.1f knots", UTILS.MpsToKnots(Ws))
local tP=string.format("%.2f inHg", UTILS.hPa2inHg(P))
@@ -17504,7 +17504,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
if playerData.step==AIRBOSS.PatternStep.HOLDING and playerData.case>1 then
-- Get inverse magnetic radial potential offset.
local radial=self:GetRadial(playerData.case, true, true, true)
stacktext=stacktext..string.format("Select TACAN %03d°, %d DME\n", radial, angels+15)
stacktext=stacktext..string.format("Select TACAN %03d°, %d DME\n", radial, angels+15)
end
end
@@ -17545,7 +17545,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
local brc=self:GetBRC()
-- Help player to find its way to the initial zone.
text=text..string.format("\nTo Initial: Fly heading %03d° for %.1f NM and turn to BRC %03d°", flyhdg, flydist, brc)
text=text..string.format("\nTo Initial: Fly heading %03d° for %.1f NM and turn to BRC %03d°", flyhdg, flydist, brc)
elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then
@@ -17560,7 +17560,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
local hdg=self:GetRadial(playerData.case, true, true, true)
-- Help player to find its way to the initial zone.
text=text..string.format("\nTo Platform: Fly heading %03d° for %.1f NM and turn to %03d°", flyhdg, flydist, hdg)
text=text..string.format("\nTo Platform: Fly heading %03d° for %.1f NM and turn to %03d°", flyhdg, flydist, hdg)
end

View File

@@ -1,4 +1,3 @@
--- **Ops** -- Combat Search and Rescue.
--
-- ===
@@ -23,7 +22,7 @@
-- @module Ops.CSAR
-- @image OPS_CSAR.jpg
-- Date: July 2021
-- Date: Aug 2021
-------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@@ -70,6 +69,7 @@
--
-- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms.
-- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only!
-- self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
-- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
-- self.autosmokedistance = 1000 -- distance for autosmoke
-- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
@@ -95,6 +95,8 @@
-- self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
-- self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
-- self.pilotmustopendoors = false -- switch to true to enable check of open doors
-- -- (added 0.1.9)
-- self.suppressmessages = false -- switch off all messaging if you want to do your own
--
-- ## 2.1 Experimental Features
--
@@ -214,26 +216,6 @@ CSAR = {
-- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process
-- @field #boolean alive Group is alive or dead/rescued
--
--- Updated and sorted list of known NDB beacons (in kHz!) from the available maps.
--[[ Moved to Utils
-- @field #CSAR.SkipFrequencies
CSAR.SkipFrequencies = {
214,274,291.5,295,297.5,
300.5,304,307,309.5,311,312,312.5,316,
320,324,328,329,330,336,337,
342,343,348,351,352,353,358,
363,365,368,372.5,374,
380,381,384,389,395,396,
414,420,430,432,435,440,450,455,462,470,485,
507,515,520,525,528,540,550,560,570,577,580,602,625,641,662,670,680,682,690,
705,720,722,730,735,740,745,750,770,795,
822,830,862,866,
905,907,920,935,942,950,995,
1000,1025,1030,1050,1065,1116,1175,1182,1210
}
--]]
--- All slot / Limit settings
-- @type CSAR.AircraftType
@@ -251,7 +233,7 @@ CSAR.AircraftType["Mi-24V"] = 8
--- CSAR class version.
-- @field #string version
CSAR.version="0.1.8r3"
CSAR.version="0.1.10r3"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -361,6 +343,7 @@ function CSAR:New(Coalition, Template, Alias)
self.loadtimemax = 135 -- seconds
self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK!
self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase
self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
self.max_units = 6 --max number of pilots that can be carried
self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below
self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names!
@@ -378,6 +361,7 @@ function CSAR:New(Coalition, Template, Alias)
self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
self.pilotmustopendoors = false -- switch to true to enable check on open doors
self.suppressmessages = false
-- WARNING - here\'ll be dragons
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
@@ -642,7 +626,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
local _typeName = _typeName or "Pilot"
if not noMessage then
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, 10)
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, self.messageTime)
end
if _freq then
@@ -806,8 +790,7 @@ function CSAR:_EventHandler(EventData)
if self:_DoubleEjection(_unitname) then
return
end
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, 10)
--local m = MESSAGE:New("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!",10,"Info"):ToCoalition(self.coalition)
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, self.messageTime)
else
self:T(self.lid .. " Pilot has not taken off, ignore")
end
@@ -894,11 +877,14 @@ function CSAR:_EventHandler(EventData)
end
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true)
--[[
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_event.IniUnitName) then
self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true)
else
self:_RescuePilots(_unit)
end
--]]
else
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
end
@@ -1112,7 +1098,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
player = grouptable.player,
}
_woundedGroup:Destroy()
_woundedGroup:Destroy(false)
self:_RemoveNameFromDownedPilots(_woundedGroupName,true)
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true)
@@ -1143,6 +1129,8 @@ end
-- @return #boolean outcome The outcome.
function CSAR:_IsLoadingDoorOpen( unit_name )
self:T(self.lid .. " _IsLoadingDoorOpen")
--[[
local ret_val = false
local unit = Unit.getByName(unit_name)
if unit ~= nil then
@@ -1174,8 +1162,9 @@ function CSAR:_IsLoadingDoorOpen( unit_name )
return ret_val
end -- nil
--]]
return UTILS.IsLoadingDoorOpen(unit_name)
return false
end
--- (Internal) Function to check if heli is close to group.
@@ -1314,7 +1303,8 @@ end
-- @param #CSAR self
-- @param #string heliname Heli name
-- @param #string groupname Group name
function CSAR:_ScheduledSARFlight(heliname,groupname)
-- @param #boolean isairport If true, EVENT.Landing took place at an airport or FARP
function CSAR:_ScheduledSARFlight(heliname,groupname, isairport)
self:T(self.lid .. " _ScheduledSARFlight")
self:T({heliname,groupname})
local _heliUnit = self:_GetSARHeli(heliname)
@@ -1337,8 +1327,8 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
return
end
if _dist < 200 and _heliUnit:InAir() == false then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(heliname) then
if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true)
else
self:_RescuePilots(_heliUnit)
@@ -1347,7 +1337,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
end
--queue up
self:__Returning(-5,heliname,_woundedGroupName)
self:__Returning(-5,heliname,_woundedGroupName, isairport)
return self
end
@@ -1403,7 +1393,9 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak)
local group = _unit:GetGroup()
local _clear = _clear or nil
local _time = _time or self.messageTime
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
if not self.suppressmessages then
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
end
-- integrate SRS
if _speak and self.useSRS then
local srstext = SOUNDTEXT:New(_text)
@@ -1501,10 +1493,17 @@ function CSAR:_GetClosestDownedPilot(_heli)
local _shortestDistance = -1
local _distance = 0
local _closestGroupInfo = nil
local _heliCoord = _heli:GetCoordinate()
local _heliCoord = _heli:GetCoordinate() or _heli:GetCoordinate()
if _heliCoord == nil then
self:E("****Error obtaining coordinate!")
return nil
end
local DownedPilotsTable = self.downedPilots
for _, _groupInfo in pairs(DownedPilotsTable) do
for _, _groupInfo in UTILS.spairs(DownedPilotsTable) do
--for _, _groupInfo in pairs(DownedPilotsTable) do
local _woundedName = _groupInfo.name
local _tempWounded = _groupInfo.group
@@ -1570,12 +1569,11 @@ end
-- @param #number _messagetime How long to show.
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime)
self:T(self.lid .. " _DisplayToAllSAR")
local messagetime = _messagetime or self.messageTime
for _, _unitName in pairs(self.csarUnits) do
local _unit = self:_GetSARHeli(_unitName)
if _unit then
if not _messagetime then
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
end
if _unit and not self.suppressmessages then
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
end
end
return self
@@ -1747,9 +1745,21 @@ end
function CSAR:_GetDistance(_point1, _point2)
self:T(self.lid .. " _GetDistance")
if _point1 and _point2 then
local distance = _point1:DistanceFromPointVec2(_point2)
return distance
local distance1 = _point1:Get2DDistance(_point2)
local distance2 = _point1:DistanceFromPointVec2(_point2)
self:I({dist1=distance1, dist2=distance2})
if distance1 and type(distance1) == "number" then
return distance1
elseif distance2 and type(distance2) == "number" then
return distance2
else
self:E("*****Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
else
self:E("******Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
end
@@ -1915,19 +1925,19 @@ end
-- @param #CSAR self
function CSAR:_CheckDownedPilotTable()
local pilots = self.downedPilots
for _,_entry in pairs (pilots) do
self:T("Checking for " .. _entry.name)
self:T({entry=_entry})
local group = _entry.group
if not group:IsAlive() then
self:T("Group is dead")
if _entry.alive == true then
self:T("Switching .alive to false")
local npilots = {}
for _ind,_entry in pairs(pilots) do
local _group = _entry.group
if _group:IsAlive() then
npilots[_ind] = _entry
else
if _entry.alive then
self:__KIA(1,_entry.desc)
self:_RemoveNameFromDownedPilots(_entry.name,true)
end
end
end
self.downedPilots = npilots
return self
end
@@ -2047,9 +2057,10 @@ end
-- @param #string To To state.
-- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot\'s group.
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname)
-- @param #boolean IsAirport True if heli has landed on an AFB (from event land).
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname, IsAirPort)
self:T({From, Event, To, Heliname, Woundedgroupname})
self:_ScheduledSARFlight(Heliname,Woundedgroupname)
self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort)
return self
end

View File

@@ -22,7 +22,7 @@
-- @module Ops.CTLD
-- @image OPS_CTLD.jpg
-- Date: July 2021
-- Date: Aug 2021
do
------------------------------------------------------
@@ -264,7 +264,7 @@ do
-- Add zones for loading troops and crates and dropping, building crates
--
-- -- Add a zone of type LOAD to our setup. Players can load troops and crates.
-- -- "Loadzone" is the name of the zone from the ME. Players can load, if they are inside of the zone.
-- -- "Loadzone" is the name of the zone from the ME. Players can load, if they are inside the zone.
-- -- Smoke and Flare color for this zone is blue, it is active (can be used) and has a radio beacon.
-- my_ctld:AddCTLDZone("Loadzone",CTLD.CargoZoneType.LOAD,SMOKECOLOR.Blue,true,true)
--
@@ -279,6 +279,13 @@ do
--
-- my_ctld:AddCTLDZone("Movezone2",CTLD.CargoZoneType.MOVE,SMOKECOLOR.White,true,true)
--
-- -- Add a zone of type SHIP to our setup. Players can load troops and crates from this ship
-- -- "Tarawa" is the unitname (callsign) of the ship from the ME. Players can load, if they are inside the zone.
-- -- The ship is 240 meters long and 20 meters wide.
-- -- Note that smoke, flares, beacons don't work for this type of loadzone (yet). Also, you need to adjust
-- -- the max hover height to deck height plus 5 meters or so for loading to work.
-- -- When the ship is moving, forcing hoverload might not be a good idea.
-- my_ctld:AddCTLDZone("Tarawa",CTLD.CargoZoneType.SHIP,SMOKECOLOR.Blue,true,true,240,20)
--
-- ## 2. Options
--
@@ -511,11 +518,13 @@ CTLD = {
-- @field #string name Name of Zone.
-- @field #string color Smoke color for zone, e.g. SMOKECOLOR.Red.
-- @field #boolean active Active or not.
-- @field #string type Type of zone, i.e. load,drop,move
-- @field #string type Type of zone, i.e. load,drop,move,ship
-- @field #boolean hasbeacon Create and run radio beacons if active.
-- @field #table fmbeacon Beacon info as #CTLD.ZoneBeacon
-- @field #table uhfbeacon Beacon info as #CTLD.ZoneBeacon
-- @field #table vhfbeacon Beacon info as #CTLD.ZoneBeacon
-- @field #number shiplength For ships - length of ship
-- @field #number shipwidth For ships - width of ship
--- Zone Type Info.
-- @type CTLD.CargoZoneType
@@ -523,6 +532,7 @@ CTLD.CargoZoneType = {
LOAD = "load",
DROP = "drop",
MOVE = "move",
SHIP = "ship",
}
--- Buildable table info.
@@ -557,7 +567,7 @@ CTLD.UnitTypes = {
--- CTLD class version.
-- @field #string version
CTLD.version="0.1.4r3"
CTLD.version="0.1.5a1"
--- Instantiate a new CTLD.
-- @param #CTLD self
@@ -643,6 +653,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
self.pickupZones = {}
self.dropOffZones = {}
self.wpZones = {}
self.shipZones = {}
-- Cargo
self.Cargo_Crates = {}
@@ -659,6 +670,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
-- setup
self.CrateDistance = 30 -- list/load crates in this radius
self.ExtractFactor = 3.33 -- factor for troops extraction, i.e. CrateDistance * Extractfactor
self.prefixes = Prefixes or {"Cargoheli"}
--self.I({prefixes = self.prefixes})
self.useprefix = true
@@ -927,6 +939,9 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype)
local hoverload = self:CanHoverLoad(Unit)
-- check if we are in LOAD zone
local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
if not inzone then
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
if not inzone then
self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group)
if not self.debug then return self end
@@ -1117,7 +1132,9 @@ end
end
end
if nearestGroup == nil or nearestDistance > self.CrateDistance then
local extractdistance = self.CrateDistance * self.ExtractFactor
if nearestGroup == nil or nearestDistance > extractdistance then
self:_SendMessage("No units close enough to extract!", 10, false, Group)
return self
end
@@ -1165,7 +1182,7 @@ end
-- clean up:
table.remove(self.DroppedTroops, nearestGroupIndex)
nearestGroup:Destroy()
nearestGroup:Destroy(false)
end
return self
end
@@ -1183,8 +1200,13 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
-- check if we are in LOAD zone
local inzone = false
local drop = drop or false
local ship = nil
local width = 20
if not drop then
inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
if not inzone then
inzone, ship, zone, distance, width = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
else
if self.dropcratesanywhere then -- #1570
inzone = true
@@ -1230,7 +1252,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
for i=1,50 do
math.random(90,270)
end
local rheading = math.floor(math.random(90,270) * heading + 1 / 360)
local rheading = math.floor(((math.random(90,270) * heading) + 1) / 360)
if not IsHerc then
rheading = rheading + 180 -- mirror for Helis
end
@@ -1238,10 +1260,25 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
local cratecoord = position:Translate(cratedistance,rheading)
local cratevec2 = cratecoord:GetVec2()
self.CrateCounter = self.CrateCounter + 1
if type(ship) == "string" then
self:T("Spawning on ship "..ship)
local Ship = UNIT:FindByName(ship)
local shipcoord = Ship:GetCoordinate()
local unitcoord = Unit:GetCoordinate()
local dist = shipcoord:Get2DDistance(unitcoord)
dist = dist - (20 + math.random(1,10))
local width = width / 2
local Offy = math.random(-width,width)
self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType("container_cargo","Cargos",country.id.GERMANY)
--:InitCoordinate(cratecoord)
:InitLinkToUnit(Ship,dist,Offy,0)
:Spawn(270,cratealias)
else
self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType("container_cargo","Cargos",country.id.GERMANY)
:InitCoordinate(cratecoord)
--:InitLinkToUnit(Unit,OffsetX,OffsetY,OffsetAngle)
:Spawn(270,cratealias)
end
local templ = cargotype:GetTemplates()
local sorte = cargotype:GetType()
self.CargoCounter = self.CargoCounter +1
@@ -1390,8 +1427,13 @@ function CTLD:_LoadCratesNearby(Group, Unit)
-- get nearby crates
local finddist = self.CrateDistance or 30
local nearcrates,number = self:_FindCratesNearby(Group,Unit,finddist) -- #table
if number == 0 or numberonboard == cratelimit then
self:_SendMessage("Sorry no loadable crates nearby or fully loaded!", 10, false, Group)
if number == 0 and self.hoverautoloading then
return -- exit
elseif number == 0 then
self:_SendMessage("Sorry no loadable crates nearby!", 10, false, Group)
return -- exit
elseif numberonboard == cratelimit then
self:_SendMessage("Sorry no fully loaded!", 10, false, Group)
return -- exit
else
-- go through crates and load
@@ -1415,14 +1457,14 @@ function CTLD:_LoadCratesNearby(Group, Unit)
table.insert(loaded.Cargo, crate)
table.insert(crateidsloaded,crate:GetID())
-- destroy crate
crate:GetPositionable():Destroy()
crate:GetPositionable():Destroy(false)
crate.Positionable = nil
self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()), 10, false, Group)
self:_UpdateUnitCargoMass(Unit)
self:__CratesPickedUp(1, Group, Unit, crate)
end
end
self.Loaded_Cargo[unitname] = loaded
self:_UpdateUnitCargoMass(Unit)
-- clean up real world crates
local existingcrates = self.Spawned_Cargo -- #table
local newexcrates = {}
@@ -1474,9 +1516,10 @@ function CTLD:_UpdateUnitCargoMass(Unit)
self:T(self.lid .. " _UpdateUnitCargoMass")
local calculatedMass = self:_GetUnitCargoMass(Unit)
Unit:SetUnitInternalCargo(calculatedMass)
local report = REPORT:New("Loadmaster report")
report:Add("Carrying " .. calculatedMass .. "Kg")
self:_SendMessage(report:Text(),10,false,Unit:GetGroup())
--local report = REPORT:New("Loadmaster report")
--report:Add("Carrying " .. calculatedMass .. "Kg")
--self:_SendMessage(report:Text(),10,false,Unit:GetGroup())
return self
end
--- (Internal) Function to list loaded cargo.
@@ -1557,6 +1600,9 @@ function CTLD:_UnloadTroops(Group, Unit)
-- check if we are in LOAD zone
local droppingatbase = false
local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
if not inzone then
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase = true
end
@@ -1893,17 +1939,16 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation)
end
for _,_template in pairs(temptable) do
self.TroopCounter = self.TroopCounter + 1
if canmove then
local alias = string.format("%s-%d", _template, math.random(1,100000))
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(true,20,2)
:InitDelayOff()
:SpawnFromVec2(randomcoord)
if canmove then
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(true,20,2)
:InitDelayOff()
:SpawnFromVec2(randomcoord)
else -- don't random position of e.g. SAM units build as FOB
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
--:InitRandomizeUnits(true,20,2)
:InitDelayOff()
:SpawnFromVec2(randomcoord)
:InitDelayOff()
:SpawnFromVec2(randomcoord)
end
if self.movetroopstowpzone and canmove then
self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter])
@@ -1967,7 +2012,7 @@ function CTLD:_CleanUpCrates(Crates,Build,Number)
if name == nametype then -- matching crate type
table.insert(destIDs,thisID)
found = found + 1
nowcrate:GetPositionable():Destroy()
nowcrate:GetPositionable():Destroy(false)
nowcrate.Positionable = nil
end
if found == numberdest then break end -- got enough
@@ -2133,6 +2178,8 @@ function CTLD:AddZone(Zone)
table.insert(self.pickupZones,zone)
elseif zone.type == CTLD.CargoZoneType.DROP then
table.insert(self.dropOffZones,zone)
elseif zone.type == CTLD.CargoZoneType.SHIP then
table.insert(self.shipZones,zone)
else
table.insert(self.wpZones,zone)
end
@@ -2158,6 +2205,8 @@ function CTLD:ActivateZone(Name,ZoneType,NewState)
table = self.pickupZones
elseif ZoneType == CTLD.CargoZoneType.DROP then
table = self.dropOffZones
elseif ZoneType == CTLD.CargoZoneType.SHIP then
table = self.shipZones
else
table = self.wpZones
end
@@ -2255,8 +2304,10 @@ end
-- @param #number Color Smoke/Flare color e.g. #SMOKECOLOR.Red
-- @param #string Active Is this zone currently active?
-- @param #string HasBeacon Does this zone have a beacon if it is active?
-- @param #number Shiplength Length of Ship for shipzones
-- @param #number Shipwidth Width of Ship for shipzones
-- @return #CTLD self
function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon)
function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Shipwidth)
self:T(self.lid .. " AddCTLDZone")
local ctldzone = {} -- #CTLD.CargoZone
@@ -2276,6 +2327,11 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon)
ctldzone.vhfbeacon = nil
end
if Type == CTLD.CargoZoneType.SHIP then
ctldzone.shiplength = Shiplength or 100
ctldzone.shipwidth = Shipwidth or 10
end
self:AddZone(ctldzone)
return self
end
@@ -2367,10 +2423,12 @@ end
-- @param #CTLD.CargoZoneType Zonetype Zonetype
-- @return #boolean Outcome Is in zone or not
-- @return #string name Closest zone name
-- @return #string zone Closest Core.Zone#ZONE object
-- @return Core.Zone#ZONE zone Closest Core.Zone#ZONE object
-- @return #number distance Distance to closest zone
-- @return #number width Radius of zone or width of ship
function CTLD:IsUnitInZone(Unit,Zonetype)
self:T(self.lid .. " IsUnitInZone")
self:T(Zonetype)
local unitname = Unit:GetName()
local zonetable = {}
local outcome = false
@@ -2378,6 +2436,8 @@ function CTLD:IsUnitInZone(Unit,Zonetype)
zonetable = self.pickupZones -- #table
elseif Zonetype == CTLD.CargoZoneType.DROP then
zonetable = self.dropOffZones -- #table
elseif Zonetype == CTLD.CargoZoneType.SHIP then
zonetable = self.shipZones -- #table
else
zonetable = self.wpZones -- #table
end
@@ -2386,16 +2446,29 @@ function CTLD:IsUnitInZone(Unit,Zonetype)
local colorret = nil
local maxdist = 1000000 -- 100km
local zoneret = nil
local zonewret = nil
local zonenameret = nil
for _,_cargozone in pairs(zonetable) do
local czone = _cargozone -- #CTLD.CargoZone
local unitcoord = Unit:GetCoordinate()
local zonename = czone.name
local zone = ZONE:FindByName(zonename)
zonecoord = zone:GetCoordinate()
local active = czone.active
local color = czone.color
local zoneradius = zone:GetRadius()
local zone = nil
local zoneradius = 100
local zonewidth = 20
if Zonetype == CTLD.CargoZoneType.SHIP then
self:T("Checking Type Ship: "..zonename)
zone = UNIT:FindByName(zonename)
zonecoord = zone:GetCoordinate()
zoneradius = czone.shiplength
zonewidth = czone.shipwidth
else
zone = ZONE:FindByName(zonename)
zonecoord = zone:GetCoordinate()
zoneradius = zone:GetRadius()
zonewidth = zoneradius
end
local distance = self:_GetDistance(zonecoord,unitcoord)
if distance <= zoneradius and active then
outcome = true
@@ -2404,10 +2477,15 @@ function CTLD:IsUnitInZone(Unit,Zonetype)
maxdist = distance
zoneret = zone
zonenameret = zonename
zonewret = zonewidth
colorret = color
end
end
return outcome, zonenameret, zoneret, maxdist
if Zonetype == CTLD.CargoZoneType.SHIP then
return outcome, zonenameret, zoneret, maxdist, zonewret
else
return outcome, zonenameret, zoneret, maxdist
end
end
--- User function - Start smoke in a zone close to the Unit.
@@ -2590,6 +2668,9 @@ end
self:T(self.lid .. " CanHoverLoad")
if self:IsHercules(Unit) then return false end
local outcome = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) and self:IsCorrectHover(Unit)
if not outcome then
outcome = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) --and self:IsCorrectHover(Unit)
end
return outcome
end
@@ -2651,7 +2732,7 @@ end
if self.hoverautoloading then
for _,_pilot in pairs (self.CtldUnits) do
local Unit = UNIT:FindByName(_pilot)
self:AutoHoverLoad(Unit)
if self:CanHoverLoad(Unit) then self:AutoHoverLoad(Unit) end
end
end
return self
@@ -2683,7 +2764,7 @@ end
-- @return #CTLD self
function CTLD:onafterStart(From, Event, To)
self:T({From, Event, To})
self:I(self.lid .. "Started.")
self:I(self.lid .. "Started ("..self.version..")")
if self.useprefix or self.enableHercules then
local prefix = self.prefixes
if self.enableHercules then

View File

@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
["8000"] = { "8000", 0.92 },
["9000"] = { "9000", 0.87 },
["Ñ<EFBFBD>Ñепени"] = { "degrees", 0.5 },
["километров"] = { "kilometers", 0.65 },
["градусы"] = { "degrees", 0.5 },
["километры"] = { "kilometers", 0.65 },
["km"] = { "kilometers", 0.65 },
["миль"] = { "miles", 0.45 },
["мили"] = { "miles", 0.45 },
["mi"] = { "miles", 0.45 },
["метры"] = { "meters", 0.41 },
["метров"] = { "meters", 0.41 },
["m"] = { "meters", 0.41 },
["ноги"] = { "feet", 0.37 },
["ноги"] = { "feet", 0.37 },
["br"] = { "br", 1.1 },
["bra"] = { "bra", 0.3 },
["возвращение на базу"] = { "returning_to_base", 1.40 },
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
["перехват боги"] = { "intercepting_bogeys", 1.22 },
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
["привлечение болотных птиц"] = { "engaging_bogeys", 1.68 },
["колёса вверх..."] = { "wheels_up", 0.92 },
["посадка на базу"] = { "landing at base", 1.04 },
["патрулирование"] = { "patrolling", 0.96 },
["возвращаÑ<EFBFBD>Ñ<EFBFBD>ÑŒ на базу"] = { "returning_to_base", 1.40 },
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
["перехват Ñ<>амолеÑов"] = { "intercepting_bogeys", 1.22 },
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
["захватывающие Ñ<>амолеÑÑ"] = { "engaging_bogeys", 1.68 },
["колеÑ<EFBFBD>а вверх"] = { "wheels_up", 0.92 },
["поÑ<EFBFBD>адка на базу"] = { "landing at base", 1.04 },
["патрулирующий"] = { "patrolling", 0.96 },
["за"] = { "for", 0.27 },
["и"] = { "and", 0.17 },
["в"] = { "at", 0.19 },
["dot"] = { "dot", 0.51 },
["defender"] = { "defender", 0.45 },
["для"] = { "for", 0.27 },
["и"] = { "and", 0.17 },
["на сайте"] = { "at", 0.19 },
["точка"] = { "dot", 0.51 },
["защитник"] = { "defender", 0.45 },
}
--- Create a new RADIOSPEECH object for a given radio frequency/modulation.

View File

@@ -491,7 +491,7 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end
-- 024<EFBFBD> 23' 12"N or 024<EFBFBD> 23' 12.03"N
-- 024° 23' 12"N or 024° 23' 12.03"N
return string.format('%03d°', latDeg)..string.format('%02d', latMin)..'\''..string.format(secFrmtStr, latSec)..'"'..latHemi..' '
.. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
@@ -1510,7 +1510,7 @@ end
--@return #table
function UTILS.ShuffleTable(t)
if t == nil or type(t) ~= "table" then
BASE:I("Error in ShuffleTable: Missing or wrong tyåe of Argument")
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
return
end
math.random()
@@ -1535,8 +1535,8 @@ function UTILS.IsLoadingDoorOpen( unit_name )
if unit ~= nil then
local type_name = unit:getTypeName()
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
ret_val = true
end