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

View File

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

View File

@@ -64,20 +64,24 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 ) self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
self:SetStartState( "Unloaded" ) 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", "*" ) -- Unboarding
self:AddTransition( "Loaded", "Deploy", "*" ) self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Loaded", "Loaded" ) self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "Unboarding", "Pickup", "Unloaded" ) self:AddTransition( "*", "Orbit" , "*" )
self:AddTransition( "Unloaded", "Unboard", "Unloaded" ) self:AddTransition( "*", "Destroyed", "*" )
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" ) self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "PickedUp", "*" ) self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" ) -- RTB
self:AddTransition( "*", "Home" , "*" ) self:AddTransition( "*", "Home" , "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER --- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup -- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
@@ -207,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self:SetCarrier( Helicopter ) self:SetCarrier( Helicopter )
self.landingspeed = 15 -- kph
self.landingheight = 5.5 -- meter
return self return self
end end
@@ -255,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
return self return self
end 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 #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter -- @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. -- 1 - When the helo lands normally on the ground.
-- 2 - when the helo is hit and goes RTB or even when it is destroyed. -- 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! -- 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)! -- 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 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( Helicopter:GetPointVec2() )
self:Load( self.PickupZone ) self:Load( self.PickupZone )
self.RoutePickup = false self.RoutePickup = false
@@ -285,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
end end
if self.RouteDeploy == true then 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:Unload( self.DeployZone )
self.RouteDeploy = false self.RouteDeploy = false
end 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. --- 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 #FOX self
-- @param DCS#Weapon weapon The weapon. -- @param DCS#Weapon weapon The weapon.
-- @return #number Notching heading right, 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<EFBFBD>. -- @return #number Notching heading left, i.e. missile heading -90°.
function FOX:_GetNotchingHeadings(weapon) function FOX:_GetNotchingHeadings(weapon)
if weapon then if weapon then

View File

@@ -5,7 +5,7 @@
-- ## Features: -- ## Features:
-- --
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes. -- * 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 a missile would have killed your aircraft.
-- * Provide alerts when the missile self destructs. -- * Provide alerts when the missile self destructs.
-- * Enable / Disable and Configure the Missile Trainer using the various menu options. -- * 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 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 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())) 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 -- Get elevation
local elevation=coord:GetLandHeight() local elevation=coord:GetLandHeight()
local eltxt=string.format("%d m", elevation) local eltxt=string.format("%d m", elevation)
if _settings:IsImperial() then if not _settings:IsMetric() then
elevation=UTILS.MetersToFeet(elevation) elevation=UTILS.MetersToFeet(elevation)
eltxt=string.format("%d ft", elevation) eltxt=string.format("%d ft", elevation)
end end

View File

@@ -92,7 +92,7 @@ function SEAD:New( SEADGroupPrefixes )
end end
self:HandleEvent( EVENTS.Shot, self.HandleEventShot ) self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.2.8") self:I("*** SEAD - Started Version 0.2.9")
return self return self
end end
@@ -102,7 +102,7 @@ end
-- @return #SEAD self -- @return #SEAD self
function SEAD:UpdateSet( SEADGroupPrefixes ) function SEAD:UpdateSet( SEADGroupPrefixes )
self:F( SEADGroupPrefixes ) self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
@@ -120,7 +120,7 @@ end
-- @param #number range Set the engagement range in percent, e.g. 50 -- @param #number range Set the engagement range in percent, e.g. 50
-- @return self -- @return self
function SEAD:SetEngagementRange(range) function SEAD:SetEngagementRange(range)
self:F( { range } ) self:T( { range } )
range = range or 75 range = range or 75
if range < 0 or range > 100 then if range < 0 or range > 100 then
range = 75 range = 75
@@ -135,7 +135,7 @@ end
-- @param #string WeaponName -- @param #string WeaponName
-- @return #boolean Returns true for a match -- @return #boolean Returns true for a match
function SEAD:_CheckHarms(WeaponName) function SEAD:_CheckHarms(WeaponName)
self:F( { WeaponName } ) self:T( { WeaponName } )
local hit = false local hit = false
for _,_name in pairs (SEAD.Harms) do for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end 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 local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
if _targetUnit and _targetUnit:IsAlive() then if _targetUnit and _targetUnit:IsAlive() then
local _targetMimgroup = _targetUnit:GetGroup() local _targetMimgroup = _targetUnit:GetGroup()
local _targetMimgroupName = _targetMimgroup:GetName() -- group name _targetMimgroupName = _targetMimgroup:GetName() -- group name
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill --local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
self:T( self.SEADGroupPrefixes ) self:T( self.SEADGroupPrefixes )
self:T( _targetMimgroupName ) self:T( _targetMimgroupName )
@@ -174,6 +174,7 @@ function SEAD:HandleEventShot( EventData )
-- see if we are shot at -- see if we are shot at
local SEADGroupFound = false local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( SEADGroupPrefix )
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
SEADGroupFound = true SEADGroupFound = true
self:T( '*** SEAD - Group Found' ) 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. -- 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 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. -- 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°). -- 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. -- 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 -- ## Glideslope Error
-- --
@@ -876,7 +876,7 @@
-- The graph displays the glideslope error (GSE) as a function of the distance to the carrier. -- 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 -- 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. -- At his point further corrections are necessary.
-- --
-- ## Angle of Attack -- ## Angle of Attack
@@ -2438,7 +2438,7 @@ end
-- @param #number duration Default duration of the recovery in minutes. Default 30 min. -- @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 #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 #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 -- @return #AIRBOSS self
function AIRBOSS:SetMenuRecovery(duration, windondeck, uturn, offset) function AIRBOSS:SetMenuRecovery(duration, windondeck, uturn, offset)
@@ -3878,7 +3878,7 @@ function AIRBOSS:_CheckRecoveryTimes()
-- Check if time is less than 5 minutes. -- Check if time is less than 5 minutes.
if nextwindow.WIND and nextwindow.START-time<self.dTturn and not self.turnintowind then 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 hdg=self:GetHeading()
local wind=self:GetHeadingIntoWind() local wind=self:GetHeadingIntoWind()
local delta=self:_GetDeltaHeading(hdg, wind) local delta=self:_GetDeltaHeading(hdg, wind)
@@ -3896,7 +3896,7 @@ function AIRBOSS:_CheckRecoveryTimes()
end end
--Debug info --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. -- 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) 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. -- For case 1 we want the BRC but above routine return FB.
radial=self:GetBRC() radial=self:GetBRC()
end 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, "") self:MessageToPlayer(flight, text, nil, "")
end end
@@ -9346,7 +9346,7 @@ function AIRBOSS:_Initial(playerData)
-- Hook down for students. -- Hook down for students.
if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then 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 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 else
hint=hint.." - Hook down!" hint=hint.." - Hook down!"
end end
@@ -11331,15 +11331,15 @@ function AIRBOSS:_AttitudeMonitor(playerData)
-- Output -- Output
local text=string.format("Pattern step: %s", step) 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 if self.Debug then
-- Velocity vector. -- Velocity vector.
text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s", velo.x, velo.y, velo.z) text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s", velo.x, velo.y, velo.z)
--Wind vector. --Wind vector.
text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s", wind.x, wind.y, wind.z) text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s", wind.x, wind.y, wind.z)
end end
text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw) 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("\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y*196.85)
local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit) local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit)
-- Get player velocity in km/h. -- Get player velocity in km/h.
local vplayer=playerData.unit:GetVelocityKMH() local vplayer=playerData.unit:GetVelocityKMH()
@@ -11360,14 +11360,14 @@ function AIRBOSS:_AttitudeMonitor(playerData)
playerData.step==AIRBOSS.PatternStep.GROOVE_IW then playerData.step==AIRBOSS.PatternStep.GROOVE_IW then
local lue=self:_Lineup(playerData.unit, true) local lue=self:_Lineup(playerData.unit, true)
local gle=self:_Glideslope(playerData.unit) local gle=self:_Glideslope(playerData.unit)
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi) 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("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units", lue, gle, self:_AoADeg2Units(playerData, aoa))
local grade, points, analysis=self:_LSOgrade(playerData) local grade, points, analysis=self:_LSOgrade(playerData)
text=text..string.format("\nTgroove=%.1f sec", self:_GetTimeInGroove(playerData)) text=text..string.format("\nTgroove=%.1f sec", self:_GetTimeInGroove(playerData))
text=text..string.format("\nGrade: %s %.1f PT - %s", grade, points, analysis) text=text..string.format("\nGrade: %s %.1f PT - %s", grade, points, analysis)
else else
text=text..string.format("\nR=%.2f NM | X=%d Z=%d m", UTILS.MetersToNM(rho), dx, dz) 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 end
MESSAGE:New(text, 1, nil , true):ToClient(playerData.client) MESSAGE:New(text, 1, nil , true):ToClient(playerData.client)
@@ -12695,7 +12695,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
if self.holdingoffset<0 then if self.holdingoffset<0 then
turn="left" turn="left"
end 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
end end
@@ -12704,7 +12704,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
if playerData.step==AIRBOSS.PatternStep.DIRTYUP then if playerData.step==AIRBOSS.PatternStep.DIRTYUP then
if playerData.difficulty==AIRBOSS.Difficulty.EASY then if playerData.difficulty==AIRBOSS.Difficulty.EASY then
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
hint=hint.."\nFAF! Checks completed. Nozzles 50°." hint=hint.."\nFAF! Checks completed. Nozzles 50°."
else else
--TODO: Tomcat? --TODO: Tomcat?
hint=hint.."\nDirty up! Hook, gear and flaps down." hint=hint.."\nDirty up! Hook, gear and flaps down."
@@ -12773,14 +12773,14 @@ function AIRBOSS:_StepHint(playerData, step)
-- Late break. -- Late break.
if step==AIRBOSS.PatternStep.LATEBREAK then if step==AIRBOSS.PatternStep.LATEBREAK then
if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B 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
end end
-- Abeam. -- Abeam.
if step==AIRBOSS.PatternStep.ABEAM then if step==AIRBOSS.PatternStep.ABEAM then
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B 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 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." hint=hint.."\nSlats/Flaps EXTENDED < 225 KIAS. DLC SELECTED. Auto Throttle IF DESIRED."
else else
@@ -13215,7 +13215,7 @@ function AIRBOSS:_Debrief(playerData)
end end
-- Re-enter message. -- 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) self:MessageToPlayer(playerData, text, "LSO", nil, nil, false, 5)
else else
@@ -13411,9 +13411,9 @@ function AIRBOSS:_CheckCollisionCoord(coordto, coordfrom)
local text="" local text=""
if clear then 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 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 end
self:T2(self.lid..text) self:T2(self.lid..text)
@@ -13473,7 +13473,7 @@ function AIRBOSS:_Pathfinder()
local collision=self:_CheckFreePathToNextWP(fromcoord) local collision=self:_CheckFreePathToNextWP(fromcoord)
-- Debug info. -- 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 path is clear, we start a little detour.
if not collision then if not collision then
@@ -13907,7 +13907,7 @@ function AIRBOSS:_CheckPatternUpdate()
-- Update if carrier moves by more than 2.5 NM. -- Update if carrier moves by more than 2.5 NM.
local Dupdate=UTILS.NMToMeters(2.5) 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 local Hupdate=5
----------------------- -----------------------
@@ -13941,7 +13941,7 @@ function AIRBOSS:_CheckPatternUpdate()
-- Check if orientation changed. -- Check if orientation changed.
local Hchange=false local Hchange=false
if math.abs(deltaHeading)>=Hupdate then 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 Hchange=true
end end
@@ -15642,7 +15642,7 @@ end
function AIRBOSS:_MarshalCallNewFinalBearing(FB) function AIRBOSS:_MarshalCallNewFinalBearing(FB)
-- Subtitle. -- Subtitle.
local text=string.format("new final bearing %03d°.", FB) local text=string.format("new final bearing %03d°.", FB)
-- Debug message. -- Debug message.
self:I(self.lid..text) self:I(self.lid..text)
@@ -15665,7 +15665,7 @@ end
function AIRBOSS:_MarshalCallCarrierTurnTo(hdg) function AIRBOSS:_MarshalCallCarrierTurnTo(hdg)
-- Subtitle. -- 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. -- Debug message.
self:I(self.lid..text) self:I(self.lid..text)
@@ -15718,11 +15718,11 @@ function AIRBOSS:_MarshalCallRecoveryStart(case)
-- Debug output. -- Debug output.
local text=string.format("Starting aircraft recovery Case %d ops.", case) local text=string.format("Starting aircraft recovery Case %d ops.", case)
if case==1 then if case==1 then
text=text..string.format(" BRC %03d°.", self:GetBRC()) text=text..string.format(" BRC %03d°.", self:GetBRC())
elseif case==2 then 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 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 end
self:T(self.lid..text) self:T(self.lid..text)
@@ -15767,7 +15767,7 @@ function AIRBOSS:_MarshalCallArrived(modex, case, brc, altitude, charlie, qfe)
local CT=UTILS.Split(clock[1], ":") local CT=UTILS.Split(clock[1], ":")
-- Subtitle text. -- 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. -- Debug message.
self:I(self.lid..text) 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, "60 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 60)
missionCommands.addCommandForGroup(gid, "90 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 90) missionCommands.addCommandForGroup(gid, "90 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 90)
local _menusetrtime=missionCommands.addSubMenuForGroup(gid, "Set Marshal Radial", _skipperPath) local _menusetrtime=missionCommands.addSubMenuForGroup(gid, "Set Marshal Radial", _skipperPath)
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, "+15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 15)
missionCommands.addCommandForGroup(gid, "0°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0) missionCommands.addCommandForGroup(gid, "", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0)
missionCommands.addCommandForGroup(gid, "-15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -15) 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, "U-turn On/Off", _skipperPath, self._SkipperRecoveryUturn, self, _unitName) 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 I", _skipperPath, self._SkipperStartRecovery, self, _unitName, 1)
missionCommands.addCommandForGroup(gid, "Start CASE II", _skipperPath, self._SkipperStartRecovery, self, _unitName, 2) missionCommands.addCommandForGroup(gid, "Start CASE II", _skipperPath, self._SkipperStartRecovery, self, _unitName, 2)
@@ -16005,7 +16005,7 @@ function AIRBOSS:_SkipperStartRecovery(_unitName, case)
-- Inform player. -- 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)) 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 if case>1 then
text=text..string.format(" Marshal radial %d°.", self.skipperOffset) text=text..string.format(" Marshal radial %d°.", self.skipperOffset)
end end
if self:IsRecovering() then if self:IsRecovering() then
text="negative, carrier is already recovering." text="negative, carrier is already recovering."
@@ -16071,7 +16071,7 @@ function AIRBOSS:_SkipperRecoveryOffset(_unitName, offset)
if playerData then if playerData then
-- Inform player. -- 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:MessageToPlayer(playerData, text, "AIRBOSS")
self.skipperOffset=offset self.skipperOffset=offset
@@ -16534,7 +16534,7 @@ function AIRBOSS:_RequestCommence(_unitName)
-- For case 1 we want the BRC but above routine return FB. -- For case 1 we want the BRC but above routine return FB.
radial=self:GetBRC() radial=self:GetBRC()
end 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 end
-- TODO: Inform section members. -- TODO: Inform section members.
@@ -17146,7 +17146,7 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
-- Only include current and future recovery windows. -- Only include current and future recovery windows.
if Tabs<recovery.STOP then if Tabs<recovery.STOP then
-- Output text. -- 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 if recovery.WIND then
recoverytext=recoverytext..string.format(" @ %.1f kts wind", recovery.SPEED) recoverytext=recoverytext..string.format(" @ %.1f kts wind", recovery.SPEED)
end end
@@ -17187,9 +17187,9 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
text=text..string.format("Case %d recovery ops\n", self.case) text=text..string.format("Case %d recovery ops\n", self.case)
else else
local radial=self:GetRadial(self.case, true, true, false) 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 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("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("Tower frequency %.3f MHz\n", self.TowerFreq)
text=text..string.format("Marshal radio %.3f MHz\n", self.MarshalFreq) 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 WodPA=UTILS.MpsToKnots(WodPA)
local WodPP=UTILS.MpsToKnots(WodPP) local WodPP=UTILS.MpsToKnots(WodPP)
local WD=string.format('%03d°', Wd) local WD=string.format('%03d°', Wd)
local Ts=string.format("%d°C",T) local Ts=string.format("%d°C",T)
local tT=string.format("%d°C",T) local tT=string.format("%d°C",T)
local tW=string.format("%.1f knots", UTILS.MpsToKnots(Ws)) local tW=string.format("%.1f knots", UTILS.MpsToKnots(Ws))
local tP=string.format("%.2f inHg", UTILS.hPa2inHg(P)) 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 if playerData.step==AIRBOSS.PatternStep.HOLDING and playerData.case>1 then
-- Get inverse magnetic radial potential offset. -- Get inverse magnetic radial potential offset.
local radial=self:GetRadial(playerData.case, true, true, true) 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
end end
@@ -17545,7 +17545,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
local brc=self:GetBRC() local brc=self:GetBRC()
-- Help player to find its way to the initial zone. -- 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 elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then
@@ -17560,7 +17560,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
local hdg=self:GetRadial(playerData.case, true, true, true) local hdg=self:GetRadial(playerData.case, true, true, true)
-- Help player to find its way to the initial zone. -- 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 end

View File

@@ -1,4 +1,3 @@
--- **Ops** -- Combat Search and Rescue. --- **Ops** -- Combat Search and Rescue.
-- --
-- === -- ===
@@ -23,7 +22,7 @@
-- @module Ops.CSAR -- @module Ops.CSAR
-- @image OPS_CSAR.jpg -- @image OPS_CSAR.jpg
-- Date: July 2021 -- Date: Aug 2021
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM --- **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.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.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.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
-- self.autosmokedistance = 1000 -- distance for autosmoke -- 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. -- 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_far = 5000 -- switch do 10 sec interval approach mode, meters
-- self.approachdist_near = 3000 -- switch to 5 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 -- 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 -- ## 2.1 Experimental Features
-- --
@@ -214,26 +216,6 @@ CSAR = {
-- @field Wrapper.Group#GROUP group Spawned group object. -- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process -- @field #number timestamp Timestamp for approach process
-- @field #boolean alive Group is alive or dead/rescued -- @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 --- All slot / Limit settings
-- @type CSAR.AircraftType -- @type CSAR.AircraftType
@@ -251,7 +233,7 @@ CSAR.AircraftType["Mi-24V"] = 8
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version="0.1.8r3" CSAR.version="0.1.10r3"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@@ -361,6 +343,7 @@ function CSAR:New(Coalition, Template, Alias)
self.loadtimemax = 135 -- seconds 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.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.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.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.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! 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_far = 5000 -- switch do 10 sec interval approach mode, meters
self.approachdist_near = 3000 -- switch to 5 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.pilotmustopendoors = false -- switch to true to enable check on open doors
self.suppressmessages = false
-- WARNING - here\'ll be dragons -- WARNING - here\'ll be dragons
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua -- 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" local _typeName = _typeName or "Pilot"
if not noMessage then 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 end
if _freq then if _freq then
@@ -806,8 +790,7 @@ function CSAR:_EventHandler(EventData)
if self:_DoubleEjection(_unitname) then if self:_DoubleEjection(_unitname) then
return return
end end
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, 10) self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, self.messageTime)
--local m = MESSAGE:New("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!",10,"Info"):ToCoalition(self.coalition)
else else
self:T(self.lid .. " Pilot has not taken off, ignore") self:T(self.lid .. " Pilot has not taken off, ignore")
end end
@@ -894,11 +877,14 @@ function CSAR:_EventHandler(EventData)
end end
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then 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 if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_event.IniUnitName) then
self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true) self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true)
else else
self:_RescuePilots(_unit) self:_RescuePilots(_unit)
end end
--]]
else else
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition())) self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
end end
@@ -1112,7 +1098,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
player = grouptable.player, player = grouptable.player,
} }
_woundedGroup:Destroy() _woundedGroup:Destroy(false)
self:_RemoveNameFromDownedPilots(_woundedGroupName,true) 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) 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. -- @return #boolean outcome The outcome.
function CSAR:_IsLoadingDoorOpen( unit_name ) function CSAR:_IsLoadingDoorOpen( unit_name )
self:T(self.lid .. " _IsLoadingDoorOpen") self:T(self.lid .. " _IsLoadingDoorOpen")
--[[
local ret_val = false local ret_val = false
local unit = Unit.getByName(unit_name) local unit = Unit.getByName(unit_name)
if unit ~= nil then if unit ~= nil then
@@ -1174,8 +1162,9 @@ function CSAR:_IsLoadingDoorOpen( unit_name )
return ret_val return ret_val
end -- nil end -- nil
--]]
return UTILS.IsLoadingDoorOpen(unit_name)
return false
end end
--- (Internal) Function to check if heli is close to group. --- (Internal) Function to check if heli is close to group.
@@ -1314,7 +1303,8 @@ end
-- @param #CSAR self -- @param #CSAR self
-- @param #string heliname Heli name -- @param #string heliname Heli name
-- @param #string groupname Group 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(self.lid .. " _ScheduledSARFlight")
self:T({heliname,groupname}) self:T({heliname,groupname})
local _heliUnit = self:_GetSARHeli(heliname) local _heliUnit = self:_GetSARHeli(heliname)
@@ -1337,8 +1327,8 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
return return
end end
if _dist < 200 and _heliUnit:InAir() == false then if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(heliname) then if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true)
else else
self:_RescuePilots(_heliUnit) self:_RescuePilots(_heliUnit)
@@ -1347,7 +1337,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
end end
--queue up --queue up
self:__Returning(-5,heliname,_woundedGroupName) self:__Returning(-5,heliname,_woundedGroupName, isairport)
return self return self
end end
@@ -1403,7 +1393,9 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak)
local group = _unit:GetGroup() local group = _unit:GetGroup()
local _clear = _clear or nil local _clear = _clear or nil
local _time = _time or self.messageTime 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 -- integrate SRS
if _speak and self.useSRS then if _speak and self.useSRS then
local srstext = SOUNDTEXT:New(_text) local srstext = SOUNDTEXT:New(_text)
@@ -1501,10 +1493,17 @@ function CSAR:_GetClosestDownedPilot(_heli)
local _shortestDistance = -1 local _shortestDistance = -1
local _distance = 0 local _distance = 0
local _closestGroupInfo = nil 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 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 _woundedName = _groupInfo.name
local _tempWounded = _groupInfo.group local _tempWounded = _groupInfo.group
@@ -1570,12 +1569,11 @@ end
-- @param #number _messagetime How long to show. -- @param #number _messagetime How long to show.
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime) function CSAR:_DisplayToAllSAR(_message, _side, _messagetime)
self:T(self.lid .. " _DisplayToAllSAR") self:T(self.lid .. " _DisplayToAllSAR")
local messagetime = _messagetime or self.messageTime
for _, _unitName in pairs(self.csarUnits) do for _, _unitName in pairs(self.csarUnits) do
local _unit = self:_GetSARHeli(_unitName) local _unit = self:_GetSARHeli(_unitName)
if _unit then if _unit and not self.suppressmessages then
if not _messagetime then self:_DisplayMessageToSAR(_unit, _message, _messagetime)
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
end
end end
end end
return self return self
@@ -1747,9 +1745,21 @@ end
function CSAR:_GetDistance(_point1, _point2) function CSAR:_GetDistance(_point1, _point2)
self:T(self.lid .. " _GetDistance") self:T(self.lid .. " _GetDistance")
if _point1 and _point2 then if _point1 and _point2 then
local distance = _point1:DistanceFromPointVec2(_point2) local distance1 = _point1:Get2DDistance(_point2)
return distance 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 else
self:E("******Cannot calculate distance!")
self:E({_point1,_point2})
return -1 return -1
end end
end end
@@ -1915,19 +1925,19 @@ end
-- @param #CSAR self -- @param #CSAR self
function CSAR:_CheckDownedPilotTable() function CSAR:_CheckDownedPilotTable()
local pilots = self.downedPilots local pilots = self.downedPilots
for _,_entry in pairs (pilots) do local npilots = {}
self:T("Checking for " .. _entry.name)
self:T({entry=_entry}) for _ind,_entry in pairs(pilots) do
local group = _entry.group local _group = _entry.group
if not group:IsAlive() then if _group:IsAlive() then
self:T("Group is dead") npilots[_ind] = _entry
if _entry.alive == true then else
self:T("Switching .alive to false") if _entry.alive then
self:__KIA(1,_entry.desc) self:__KIA(1,_entry.desc)
self:_RemoveNameFromDownedPilots(_entry.name,true)
end end
end end
end end
self.downedPilots = npilots
return self return self
end end
@@ -2047,9 +2057,10 @@ end
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot\'s 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:T({From, Event, To, Heliname, Woundedgroupname})
self:_ScheduledSARFlight(Heliname,Woundedgroupname) self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort)
return self return self
end end

View File

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

View File

@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
["8000"] = { "8000", 0.92 }, ["8000"] = { "8000", 0.92 },
["9000"] = { "9000", 0.87 }, ["9000"] = { "9000", 0.87 },
["Ñ<EFBFBD>Ñепени"] = { "degrees", 0.5 }, ["градусы"] = { "degrees", 0.5 },
["километров"] = { "kilometers", 0.65 }, ["километры"] = { "kilometers", 0.65 },
["km"] = { "kilometers", 0.65 }, ["km"] = { "kilometers", 0.65 },
["миль"] = { "miles", 0.45 }, ["мили"] = { "miles", 0.45 },
["mi"] = { "miles", 0.45 }, ["mi"] = { "miles", 0.45 },
["метры"] = { "meters", 0.41 }, ["метров"] = { "meters", 0.41 },
["m"] = { "meters", 0.41 }, ["m"] = { "meters", 0.41 },
["ноги"] = { "feet", 0.37 }, ["ноги"] = { "feet", 0.37 },
["br"] = { "br", 1.1 }, ["br"] = { "br", 1.1 },
["bra"] = { "bra", 0.3 }, ["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 }, ["для"] = { "for", 0.27 },
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 }, ["и"] = { "and", 0.17 },
["перехват Ñ<>амолеÑов"] = { "intercepting_bogeys", 1.22 }, ["на сайте"] = { "at", 0.19 },
["поражение наземной цели"] = { "engaging_ground_target", 1.53 }, ["точка"] = { "dot", 0.51 },
["захватывающие Ñ<>амолеÑÑ"] = { "engaging_bogeys", 1.68 }, ["защитник"] = { "defender", 0.45 },
["колеÑ<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 },
} }
--- Create a new RADIOSPEECH object for a given radio frequency/modulation. --- 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' secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end 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..' ' 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 .. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
@@ -1510,7 +1510,7 @@ end
--@return #table --@return #table
function UTILS.ShuffleTable(t) function UTILS.ShuffleTable(t)
if t == nil or type(t) ~= "table" then 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 return
end end
math.random() math.random()
@@ -1535,8 +1535,8 @@ function UTILS.IsLoadingDoorOpen( unit_name )
if unit ~= nil then if unit ~= nil then
local type_name = unit:getTypeName() local type_name = unit:getTypeName()
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then 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") BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
ret_val = true ret_val = true
end end