Compare commits

...

37 Commits

Author SHA1 Message Date
Applevangelist
196bcf39cf Added Falklands map stuff 2022-06-14 16:56:33 +02:00
Applevangelist
afec1c3a5b COORDINATE - additions to BRAANATO 2022-06-14 13:06:55 +02:00
Applevangelist
6025339b46 CSAR - some fixes for latest open beta 2022-06-14 12:39:17 +02:00
Applevangelist
40c6cc59d3 Update Beacon.lua 2022-06-13 15:43:10 +02:00
Applevangelist
514e568e04 Update Beacon.lua 2022-06-13 15:39:39 +02:00
Applevangelist
2f34b0a5ed Update Beacon.lua (#1734) 2022-06-13 15:34:01 +02:00
Applevangelist
708c076885 CSAR - Put wounded group back into status green, so they run to the chopper 2022-06-12 12:47:23 +02:00
Applevangelist
f0e0b918af CTLD - More docu 2022-06-09 12:12:29 +02:00
Applevangelist
e45f5e1122 CTLD - further documentation 2022-06-09 11:46:29 +02:00
Applevangelist
932015668b Added documentation for CTLD_HERCULES 2022-06-09 10:56:20 +02:00
Applevangelist
4011bc3fe6 AIRBASE added South Atlantic 2022-06-08 20:24:45 +02:00
Applevangelist
1dcccdc434 CSAR - added a couple of more lines to go out via TTS 2022-06-07 08:56:13 +02:00
Applevangelist
3380ed9360 CSAR - added options to use Google TTS 2022-06-07 08:13:19 +02:00
Applevangelist
ed9c14e63d GROUP - changes in GetDCSGroup 2022-05-22 12:06:49 +02:00
Applevangelist
4762793adc Create README.md 2022-05-22 11:16:16 +02:00
Frank
7df3946189 Update Airboss.lua
- Wind is calculated at 15 m (not 50 m)
2022-05-20 20:16:45 +02:00
Applevangelist
d5fb75fe43 Positionable - added 6 passengers (cargo weight) to Toyota HL/LC new with 2.7.2 OB 2022-05-14 13:11:30 +02:00
Applevangelist
c0b32a5584 SRS - added hints on using Google with TTS 2022-05-13 16:37:41 +02:00
Applevangelist
e2b1276d7b Point - added option to add an SSML tag to ToStringBRAANATO 2022-05-12 14:49:30 +02:00
Applevangelist
f6aea13fae SRS - put volume in "" - just in case 2022-05-11 07:30:26 +02:00
Applevangelist
646b113c55 SRS - actually pass the volume to the command line 2022-05-11 06:19:09 +02:00
Applevangelist
58074f499f AIRBASE - corrected ["Deir_ez_Zor"] = "Deir ez-Zor" (minus doesn't work in enum) 2022-05-10 19:40:32 +02:00
Applevangelist
1483ffd7ff Correct link to demo missions 2022-05-10 16:18:00 +02:00
476th-Scaley
cdaef851a0 Update AI_Air.lua (#1729)
* Update AI_Air.lua

Altered RTB airspeed (slower) and target altitude over the airfield being returned to (higher) to produce more realistic and fuel efficient descent profiles. Leads to aircraft arriving overhead the airfield quite high and generally flying one orbit to descend to land. 

Scaley

* Create AI_Air.lua

Co-authored-by: Applevangelist <72444570+Applevangelist@users.noreply.github.com>
2022-05-10 10:10:41 +02:00
Applevangelist
04068d7117 Group - small change 2022-05-07 19:42:31 +02:00
Applevangelist
41e8ddea8c GROUP - change to GetUnits(n) to make it more robust, now returns first alive unit,actually. Similar changes to GetHeading() 2022-05-07 11:54:33 +02:00
Applevangelist
cc49791997 Fallout fixes 2022-05-06 11:45:11 +02:00
Applevangelist
ba4a8050ba AI Patrol - life check on route 2022-05-06 10:36:39 +02:00
Applevangelist
7c5067a59a UNIT Register - small fix for trains 2022-05-06 08:01:31 +02:00
Applevangelist
69eb920173 AI_CAP - more fallout from the dead units in the API 2022-05-05 17:40:00 +02:00
Applevangelist
07d761941a AI_AIR - restrict AB on RTB 2022-05-05 16:41:32 +02:00
Applevangelist
ca52585759 AI/ZONE - Some fixes for units unreachable 2022-05-05 12:07:56 +02:00
Applevangelist
decc9d09f8 docu update 2022-05-05 11:34:14 +02:00
Applevangelist
466a18447c SRS - adding volume setting and a test on OS and IO available 2022-05-05 08:57:27 +02:00
Frank
27902ee107 Update Range.lua
**RANGE**
- Fixed a couple of bugs
- Added new FSM events for strafing
- Updated docs
2022-05-04 22:36:41 +02:00
Applevangelist
8a8b806362 further event related stuff not working any more 2022-05-04 18:09:56 +02:00
Applevangelist
40bb181c78 Another nil check... 2022-05-04 13:29:40 +02:00
25 changed files with 768 additions and 478 deletions

View File

@@ -253,6 +253,9 @@ function AI_AIR:New( AIGroup )
self.IdleCount = 0 self.IdleCount = 0
self.RTBSpeedMaxFactor = 0.6
self.RTBSpeedMinFactor = 0.5
return self return self
end end
@@ -576,6 +579,19 @@ function AI_AIR.RTBHold( AIGroup, Fsm )
end end
--- Set the min and max factors on RTB speed. Use this, if your planes are heading back to base too fast. Default values are 0.5 and 0.6.
-- The RTB speed is calculated as the max speed of the unit multiplied by MinFactor (lower bracket) and multiplied by MaxFactor (upper bracket).
-- A random value in this bracket is then applied in the waypoint routing generation.
-- @param #AI_AIR self
-- @param #number MinFactor Lower bracket factor. Defaults to 0.5.
-- @param #number MaxFactor Upper bracket factor. Defaults to 0.6.
-- @return #AI_AIR self
function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
self.RTBSpeedMaxFactor = MaxFactor or 0.6
self.RTBSpeedMinFactor = MinFactor or 0.5
return self
end
--- @param #AI_AIR self --- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
@@ -590,6 +606,8 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
self:ClearTargetDistance() self:ClearTargetDistance()
--AIGroup:ClearTasks() --AIGroup:ClearTasks()
AIGroup:OptionProhibitAfterburner(true)
local EngageRoute = {} local EngageRoute = {}
--- Calculate the target route point. --- Calculate the target route point.
@@ -597,12 +615,14 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
local FromCoord = AIGroup:GetCoordinate() local FromCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!) local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
local ToTargetVec3 = ToTargetCoord:GetVec3() local ToTargetVec3 = ToTargetCoord:GetVec3()
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 ) local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
if not self.RTBMinSpeed or not self.RTBMaxSpeed then if not self.RTBMinSpeed or not self.RTBMaxSpeed then
local RTBSpeedMax = AIGroup:GetSpeedMax() local RTBSpeedMax = AIGroup:GetSpeedMax()
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 ) local RTBSpeedMaxFactor = self.RTBSpeedMaxFactor or 0.6
local RTBSpeedMinFactor = self.RTBSpeedMinFactor or 0.5
self:SetRTBSpeed( RTBSpeedMax * RTBSpeedMinFactor, RTBSpeedMax * RTBSpeedMaxFactor)
end end
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed ) local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )

View File

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

View File

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

View File

@@ -9,7 +9,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing)
-- --
-- === -- ===
-- --

View File

@@ -10,7 +10,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
-- --
-- === -- ===
-- --
@@ -428,8 +428,12 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then -- flight dead at this point
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() return self
end
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(

View File

@@ -11,7 +11,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support)
-- --
-- === -- ===
-- --
@@ -460,7 +460,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(

View File

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

View File

@@ -16,29 +16,29 @@
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon --- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
-- --
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. -- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. -- There are two types of BEACONs available : the (aircraft) TACAN Beacon and the general purpose Radio Beacon.
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is -- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very useful to simulate the battery time if your BEACON is
-- attach to a cargo crate, for exemple. -- attach to a cargo crate, for exemple.
-- --
-- ## AA TACAN Beacon usage -- ## Aircraft TACAN Beacon usage
-- --
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. -- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON.ActivateTACAN}() to set the beacon parameters and start the beacon.
-- Use @#BEACON:StopAATACAN}() to stop it. -- Use @{#BEACON.StopRadioBeacon}() to stop it.
-- --
-- ## General Purpose Radio Beacon usage -- ## General Purpose Radio Beacon usage
-- --
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with -- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. -- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON.RadioBeacon}() to set the beacon parameters and start the beacon.
-- Use @{#BEACON:StopRadioBeacon}() to stop it. -- Use @{#BEACON.StopRadioBeacon}() to stop it.
-- --
-- @type BEACON -- @type BEACON
-- @field #string ClassName Name of the class "BEACON". -- @field #string ClassName Name of the class "BEACON".
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities. -- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
BEACON = { BEACON = {
ClassName = "BEACON", ClassName = "BEACON",
Positionable = nil, Positionable = nil,
name = nil, name = nil,
} }
--- Beacon types supported by DCS. --- Beacon types supported by DCS.
@@ -95,13 +95,13 @@ BEACON.Type={
-- @field #number TACAN TACtical Air Navigation system on ground. -- @field #number TACAN TACtical Air Navigation system on ground.
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band. -- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band. -- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
-- @field #number VOR Very High Frequency Omnidirectional Range -- @field #number VOR Very High Frequency Omni-Directional Range
-- @field #number ILS_LOCALIZER ILS localizer -- @field #number ILS_LOCALIZER ILS localizer
-- @field #number ILS_GLIDESLOPE ILS glide slope. -- @field #number ILS_GLIDESLOPE ILS glide slope.
-- @field #number PRGM_LOCALIZER PRGM localizer. -- @field #number PRGM_LOCALIZER PRGM localizer.
-- @field #number PRGM_GLIDESLOPE PRGM glide slope. -- @field #number PRGM_GLIDESLOPE PRGM glide slope.
-- @field #number BROADCAST_STATION Broadcast station. -- @field #number BROADCAST_STATION Broadcast station.
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon. -- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omni-directional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band. -- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band. -- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME). -- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
@@ -131,27 +131,28 @@ BEACON.System={
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead. -- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
-- @param #BEACON self -- @param #BEACON self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #BEACON Beacon object or #nil if the POSITIONABLE is invalid. -- @return #BEACON Beacon object or #nil if the positionable is invalid.
function BEACON:New( Positionable ) function BEACON:New(Positionable)
-- Inherit BASE. -- Inherit BASE.
local self = BASE:Inherit( self, BASE:New() ) -- #BEACON local self=BASE:Inherit(self, BASE:New()) --#BEACON
-- Debug. -- Debug.
self:F( Positionable ) self:F(Positionable)
-- Set positionable. -- Set positionable.
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure POSITIONABLE is valid if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable self.Positionable = Positionable
self.name = Positionable:GetName() self.name=Positionable:GetName()
self:I( string.format( "New BEACON %s", tostring( self.name ) ) ) self:I(string.format("New BEACON %s", tostring(self.name)))
return self return self
end end
self:E( { "The passed POSITIONABLE is invalid, no BEACON created", Positionable } ) self:E({"The passed positionable is invalid, no BEACON created", Positionable})
return nil return nil
end end
--- Activates a TACAN BEACON. --- Activates a TACAN BEACON.
-- @param #BEACON self -- @param #BEACON self
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y". -- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
@@ -161,53 +162,58 @@ end
-- @param #number Duration How long will the beacon last in seconds. Omit for forever. -- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit") -- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon -- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
-- function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
function BEACON:ActivateTACAN( Channel, Mode, Message, Bearing, Duration ) self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
self:T( { channel = Channel, mode = Mode, callsign = Message, bearing = Bearing, duration = Duration } )
Mode=Mode or "Y"
-- Get frequency. -- Get frequency.
local Frequency = UTILS.TACANToFrequency( Channel, Mode ) local Frequency=UTILS.TACANToFrequency(Channel, Mode)
-- Check. -- Check.
if not Frequency then if not Frequency then
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } ) self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
return self return self
end end
-- Beacon type. -- Beacon type.
local Type = BEACON.Type.TACAN local Type=BEACON.Type.TACAN
-- Beacon system. -- Beacon system.
local System = BEACON.System.TACAN local System=BEACON.System.TACAN
-- Check if unit is an aircraft and set system accordingly. -- Check if unit is an aircraft and set system accordingly.
local AA = self.Positionable:IsAir() local AA=self.Positionable:IsAir()
if AA then if AA then
System = 5 -- NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
-- Check if "Y" mode is selected for aircraft. -- Check if "Y" mode is selected for aircraft.
if Mode ~= "Y" then if Mode=="X" then
self:E( { "WARNING: The POSITIONABLE you want to attach the AA TACAN Beacon is an aircraft: Mode should Y! The BEACON is not emitting.", self.Positionable } ) --self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y!", self.Positionable})
System=BEACON.System.TACAN_TANKER_X
else
System=BEACON.System.TACAN_TANKER_Y
end end
end end
-- Attached unit. -- Attached unit.
local UnitID = self.Positionable:GetID() local UnitID=self.Positionable:GetID()
-- Debug. -- Debug.
self:I( { string.format( "BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring( self.name ), Channel, Mode, Message, tostring( Bearing ), tostring( Duration ) ) } ) self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))})
-- Start beacon. -- Start beacon.
self.Positionable:CommandActivateBeacon( Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing ) self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
-- Stop scheduler. -- Stop scheduler.
if Duration then if Duration then
self.Positionable:DeactivateBeacon( Duration ) self.Positionable:DeactivateBeacon(Duration)
end end
return self return self
@@ -219,27 +225,28 @@ end
-- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon. -- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon.
-- @param #number Duration How long will the beacon last in seconds. Omit for forever. -- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
function BEACON:ActivateICLS( Channel, Callsign, Duration ) function BEACON:ActivateICLS(Channel, Callsign, Duration)
self:F( { Channel = Channel, Callsign = Callsign, Duration = Duration } ) self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
-- Attached unit. -- Attached unit.
local UnitID = self.Positionable:GetID() local UnitID=self.Positionable:GetID()
-- Debug -- Debug
self:T2( { "ICLS BEACON started!" } ) self:T2({"ICLS BEACON started!"})
-- Start beacon. -- Start beacon.
self.Positionable:CommandActivateICLS( Channel, UnitID, Callsign ) self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
-- Stop scheduler -- Stop scheduler
if Duration then -- Schedule the stop of the BEACON if asked by the MD if Duration then -- Schedule the stop of the BEACON if asked by the MD
self.Positionable:DeactivateBeacon( Duration ) self.Positionable:DeactivateBeacon(Duration)
end end
return self return self
end end
--- Activates a TACAN BEACON on an Aircraft. --- DEPRECATED: Please use @{BEACON:ActivateTACAN}() instead.
-- Activates a TACAN BEACON on an Aircraft.
-- @param #BEACON self -- @param #BEACON self
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels -- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon -- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
@@ -247,54 +254,55 @@ end
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever. -- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit") -- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon -- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
-- function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration ) self:F({TACANChannel, Message, Bearing, BeaconDuration})
self:F( { TACANChannel, Message, Bearing, BeaconDuration } )
local IsValid = true local IsValid = true
if not self.Positionable:IsAir() then if not self.Positionable:IsAir() then
self:E( { "The POSITIONABLE you want to attach the AA TACAN Beacon is not an aircraft! The BEACON is not emitting", self.Positionable } ) self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
IsValid = false IsValid = false
end end
local Frequency = self:_TACANToFrequency( TACANChannel, "Y" ) local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
if not Frequency then if not Frequency then
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } ) self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
IsValid = false IsValid = false
end end
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing -- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not
-- or 14 (TACAN_AA_MODE_Y) if it does not
local System local System
if Bearing then if Bearing then
System = 5 System = BEACON.System.TACAN_TANKER_Y
else else
System = 14 System = BEACON.System.TACAN_AA_MODE_Y
end end
if IsValid then -- Starts the BEACON if IsValid then -- Starts the BEACON
self:T2( { "AA TACAN BEACON started !" } ) self:T2({"AA TACAN BEACON started !"})
self.Positionable:SetCommand( { self.Positionable:SetCommand({
id = "ActivateBeacon", id = "ActivateBeacon",
params = { params = {
type = 4, type = BEACON.Type.TACAN,
system = System, system = System,
callsign = Message, callsign = Message,
AA = true,
frequency = Frequency, frequency = Frequency,
}, bearing = Bearing,
} ) modeChannel = "Y",
}
})
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil, function() SCHEDULER:New(nil,
function()
self:StopAATACAN() self:StopAATACAN()
end, {}, BeaconDuration ) end, {}, BeaconDuration)
end end
end end
@@ -307,19 +315,21 @@ end
function BEACON:StopAATACAN() function BEACON:StopAATACAN()
self:F() self:F()
if not self.Positionable then if not self.Positionable then
self:E( { "Start the beacon first before stopping it!" } ) self:E({"Start the beacon first before stoping it !"})
else else
self.Positionable:SetCommand( { self.Positionable:SetCommand({
id = 'DeactivateBeacon', id = 'DeactivateBeacon',
params = {}, params = {
} ) }
})
end end
end end
--- Activates a general purpose Radio Beacon --- Activates a general purpose Radio Beacon
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency. -- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8. -- Although any frequency could be used, only a few DCS Modules can home on radio beacons at the time of writing, i.e. the Mi-8, Huey, Gazelle etc.
-- They can home in on these specific frequencies : -- The following e.g. can home in on these specific frequencies :
-- * **Mi8** -- * **Mi8**
-- * R-828 -> 20-60MHz -- * R-828 -> 20-60MHz
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM -- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
@@ -342,63 +352,64 @@ end
-- --
-- -- Set the beacon and start it -- -- Set the beacon and start it
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60) -- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDuration ) function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
self:F( { FileName, Frequency, Modulation, Power, BeaconDuration } ) self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
local IsValid = false local IsValid = false
-- Check the filename -- Check the filename
if type( FileName ) == "string" then if type(FileName) == "string" then
if FileName:find( ".ogg" ) or FileName:find( ".wav" ) then if FileName:find(".ogg") or FileName:find(".wav") then
if not FileName:find( "l10n/DEFAULT/" ) then if not FileName:find("l10n/DEFAULT/") then
FileName = "l10n/DEFAULT/" .. FileName FileName = "l10n/DEFAULT/" .. FileName
end end
IsValid = true IsValid = true
end end
end end
if not IsValid then if not IsValid then
self:E( { "File name invalid. Maybe something wrong with the extension? ", FileName } ) self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
end end
-- Check the Frequency -- Check the Frequency
if type( Frequency ) ~= "number" and IsValid then if type(Frequency) ~= "number" and IsValid then
self:E( { "Frequency invalid. ", Frequency } ) self:E({"Frequency invalid. ", Frequency})
IsValid = false IsValid = false
end end
Frequency = Frequency * 1000000 -- Conversion to Hz Frequency = Frequency * 1000000 -- Conversion to Hz
-- Check the modulation -- Check the modulation
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then -- TODO: Maybe make this future proof if ED decides to add an other modulation ? if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self:E( { "Modulation is invalid. Use DCS's enum radio.modulation.", Modulation } ) self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
IsValid = false IsValid = false
end end
-- Check the Power -- Check the Power
if type( Power ) ~= "number" and IsValid then if type(Power) ~= "number" and IsValid then
self:E( { "Power is invalid. ", Power } ) self:E({"Power is invalid. ", Power})
IsValid = false IsValid = false
end end
Power = math.floor( math.abs( Power ) ) -- TODO: Find what is the maximum power allowed by DCS and limit power to that Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
if IsValid then if IsValid then
self:T2( { "Activating Beacon on ", Frequency, Modulation } ) self:T2({"Activating Beacon on ", Frequency, Modulation})
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID -- Note that this is looped. I have to give this transmission a unique name, I use the class ID
trigger.action.radioTransmission( FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring( self.ID ) ) trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil, function() SCHEDULER:New( nil,
self:StopRadioBeacon() function()
end, {}, BeaconDuration ) self:StopRadioBeacon()
end end, {}, BeaconDuration)
end
end end
end end
--- Stops the AA TACAN BEACON --- Stop the Radio Beacon
-- @param #BEACON self -- @param #BEACON self
-- @return #BEACON self -- @return #BEACON self
function BEACON:StopRadioBeacon() function BEACON:StopRadioBeacon()
self:F() self:F()
-- The unique name of the transmission is the class ID -- The unique name of the transmission is the class ID
trigger.action.stopRadioTransmission( tostring( self.ID ) ) trigger.action.stopRadioTransmission(tostring(self.ID))
return self return self
end end
@@ -406,21 +417,21 @@ end
-- @param #BEACON self -- @param #BEACON self
-- @param #number TACANChannel -- @param #number TACANChannel
-- @param #string TACANMode -- @param #string TACANMode
-- @return #number Frequency -- @return #number Frequecy
-- @return #nil if parameters are invalid -- @return #nil if parameters are invalid
function BEACON:_TACANToFrequency( TACANChannel, TACANMode ) function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
self:F3( { TACANChannel, TACANMode } ) self:F3({TACANChannel, TACANMode})
if type( TACANChannel ) ~= "number" then if type(TACANChannel) ~= "number" then
if TACANMode ~= "X" and TACANMode ~= "Y" then if TACANMode ~= "X" and TACANMode ~= "Y" then
return nil -- error in arguments return nil -- error in arguments
end end
end end
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137. -- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
-- I have no idea what it does but it seems to work -- I have no idea what it does but it seems to work
local A = 1151 -- 'X', channel >= 64 local A = 1151 -- 'X', channel >= 64
local B = 64 -- channel >= 64 local B = 64 -- channel >= 64
if TACANChannel < 64 then if TACANChannel < 64 then
B = 1 B = 1

View File

@@ -892,9 +892,8 @@ do -- COORDINATE
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3. -- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
-- @return DCS#Distance Distance The distance in meters. -- @return DCS#Distance Distance The distance in meters.
function COORDINATE:Get2DDistance(TargetCoordinate) function COORDINATE:Get2DDistance(TargetCoordinate)
if not TargetCoordinate then return 1000000 end
local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z} local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
local norm=UTILS.VecNorm(a) local norm=UTILS.VecNorm(a)
return norm return norm
end end
@@ -2770,8 +2769,11 @@ do -- COORDINATE
-- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from. -- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from.
-- @param #boolean Bogey Add "Bogey" at the end if true (not yet declared hostile or friendly) -- @param #boolean Bogey Add "Bogey" at the end if true (not yet declared hostile or friendly)
-- @param #boolean Spades Add "Spades" at the end if true (no IFF/VID ID yet known) -- @param #boolean Spades Add "Spades" at the end if true (no IFF/VID ID yet known)
-- @param #boolean SSML Add SSML tags speaking aspect as 0 1 2 and "brah" instead of BRAA
-- @param #boolean Angels If true, altitude is e.g. "Angels 25" (i.e., a friendly plane), else "25 thousand"
-- @param #boolean Zeros If using SSML, be aware that Google TTS will say "oh" and not "zero" for "0"; if Zeros is set to true, "0" will be replaced with "zero"
-- @return #string The BRAA text. -- @return #string The BRAA text.
function COORDINATE:ToStringBRAANATO(FromCoordinate,Bogey,Spades) function COORDINATE:ToStringBRAANATO(FromCoordinate,Bogey,Spades,SSML,Angels,Zeros)
-- Thanks to @Pikey -- Thanks to @Pikey
local BRAANATO = "Merged." local BRAANATO = "Merged."
@@ -2789,22 +2791,61 @@ do -- COORDINATE
local alt = UTILS.Round(UTILS.MetersToFeet(self.y)/1000,0)--*1000 local alt = UTILS.Round(UTILS.MetersToFeet(self.y)/1000,0)--*1000
local alttext = string.format("%d thousand",alt)
if Angels then
alttext = string.format("Angels %d",alt)
end
if alt < 1 then
alttext = "very low"
end
local track = UTILS.BearingToCardinal(bearing) or "North" local track = UTILS.BearingToCardinal(bearing) or "North"
if rangeNM > 3 then if rangeNM > 3 then
if aspect == "" then if SSML then -- google says "oh" instead of zero, be aware
BRAANATO = string.format("BRA, %03d, %d miles, Angels %d, Track %s",bearing, rangeNM, alt, track) if Zeros then
bearing = string.format("%03d",bearing)
local AngleDegText = string.gsub(bearing,"%d","%1 ") -- "0 5 1 "
AngleDegText = string.gsub(AngleDegText," $","") -- "0 5 1"
AngleDegText = string.gsub(AngleDegText,"0","zero")
if aspect == "" then
BRAANATO = string.format("brah %s, %d miles, %s, Track %s", AngleDegText, rangeNM, alttext, track)
else
BRAANATO = string.format("brah %s, %d miles, %s, %s, Track %s", AngleDegText, rangeNM, alttext, aspect, track)
end
else
if aspect == "" then
BRAANATO = string.format("brah <say-as interpret-as='characters'>%03d</say-as>, %d miles, %s, Track %s", bearing, rangeNM, alttext, track)
else
BRAANATO = string.format("brah <say-as interpret-as='characters'>%03d</say-as>, %d miles, %s, %s, Track %s", bearing, rangeNM, alttext, aspect, track)
end
end
if Bogey and Spades then
BRAANATO = BRAANATO..", Bogey, Spades."
elseif Bogey then
BRAANATO = BRAANATO..", Bogey."
elseif Spades then
BRAANATO = BRAANATO..", Spades."
else
BRAANATO = BRAANATO.."."
end
else else
BRAANATO = string.format("BRAA, %03d, %d miles, Angels %d, %s, Track %s",bearing, rangeNM, alt, aspect, track) if aspect == "" then
end BRAANATO = string.format("BRA %03d, %d miles, %s, Track %s",bearing, rangeNM, alttext, track)
if Bogey and Spades then else
BRAANATO = BRAANATO..", Bogey, Spades." BRAANATO = string.format("BRAA %03d, %d miles, %s, %s, Track %s",bearing, rangeNM, alttext, aspect, track)
elseif Bogey then end
BRAANATO = BRAANATO..", Bogey." if Bogey and Spades then
elseif Spades then BRAANATO = BRAANATO..", Bogey, Spades."
BRAANATO = BRAANATO..", Spades." elseif Bogey then
else BRAANATO = BRAANATO..", Bogey."
BRAANATO = BRAANATO.."." elseif Spades then
BRAANATO = BRAANATO..", Spades."
else
BRAANATO = BRAANATO.."."
end
end end
end end

View File

@@ -14,7 +14,7 @@
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
-- --
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
-- --

View File

@@ -1332,7 +1332,11 @@ do -- SET_GROUP
if Event.IniDCSUnit then if Event.IniDCSUnit then
local ObjectName, Object = self:FindInDatabase( Event ) local ObjectName, Object = self:FindInDatabase( Event )
if ObjectName then if ObjectName then
if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed. local size = 1
if Event.IniDCSGroup then
size = Event.IniDCSGroup:getSize()
end
if size == 1 then -- Only remove if the last unit of the group was destroyed.
self:Remove( ObjectName ) self:Remove( ObjectName )
end end
end end

View File

@@ -30,7 +30,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
-- --
-- === -- ===
-- --

View File

@@ -174,6 +174,7 @@ end
-- @param DCS#Vec3 Vec3 The point to test. -- @param DCS#Vec3 Vec3 The point to test.
-- @return #boolean true if the Vec3 is within the zone. -- @return #boolean true if the Vec3 is within the zone.
function ZONE_BASE:IsVec3InZone( Vec3 ) function ZONE_BASE:IsVec3InZone( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone return InZone
end end
@@ -1156,7 +1157,7 @@ end
-- @return #boolean true if the location is within the zone. -- @return #boolean true if the location is within the zone.
function ZONE_RADIUS:IsVec2InZone( Vec2 ) function ZONE_RADIUS:IsVec2InZone( Vec2 )
self:F2( Vec2 ) self:F2( Vec2 )
if not Vec2 then return false end
local ZoneVec2 = self:GetVec2() local ZoneVec2 = self:GetVec2()
if ZoneVec2 then if ZoneVec2 then
@@ -1174,7 +1175,7 @@ end
-- @return #boolean true if the point is within the zone. -- @return #boolean true if the point is within the zone.
function ZONE_RADIUS:IsVec3InZone( Vec3 ) function ZONE_RADIUS:IsVec3InZone( Vec3 )
self:F2( Vec3 ) self:F2( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone return InZone
@@ -1965,7 +1966,7 @@ end
-- @return #boolean true if the location is within the zone. -- @return #boolean true if the location is within the zone.
function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
self:F2( Vec2 ) self:F2( Vec2 )
if not Vec2 then return false end
local Next local Next
local Prev local Prev
local InPolygon = false local InPolygon = false
@@ -1996,6 +1997,8 @@ end
function ZONE_POLYGON_BASE:IsVec3InZone( Vec3 ) function ZONE_POLYGON_BASE:IsVec3InZone( Vec3 )
self:F2( Vec3 ) self:F2( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone return InZone

View File

@@ -591,7 +591,7 @@ do -- DETECTION_BASE
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. -- @param Wrapper.Group#GROUP Detection The Group detecting.
-- @param #number DetectionTimeStamp Time stamp of detection event. -- @param #number DetectionTimeStamp Time stamp of detection event.
function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp ) function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp )
@@ -662,7 +662,7 @@ do -- DETECTION_BASE
local DetectedObjectVec3 = DetectedObject:getPoint() local DetectedObjectVec3 = DetectedObject:getPoint()
local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z }
local DetectionGroupVec3 = Detection:GetVec3() local DetectionGroupVec3 = Detection:GetVec3() or {x=0,y=0,z=0}
local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z }
local Distance = ((DetectedObjectVec3.x - DetectionGroupVec3.x) ^ 2 + local Distance = ((DetectedObjectVec3.x - DetectionGroupVec3.x) ^ 2 +

View File

@@ -234,6 +234,16 @@
-- --
-- Strafing results are currently **not** saved. -- Strafing results are currently **not** saved.
-- --
-- # FSM Events
--
-- This class creates additional events that can be used by mission designers for custom reactions
--
-- * `EnterRange` when a player enters a range zone. See @{#RANGE.OnAfterEnterRange}
-- * `ExitRange` when a player leaves a range zone. See @{#RANGE.OnAfterExitRange}
-- * `Impact` on impact of a player's weapon on a bombing target. See @{#RANGE.OnAfterImpact}
-- * `RollingIn` when a player rolls in on a strafing target. See @{#RANGE.OnAfterRollingIn}
-- * `StrafeResult` when a player finishes a strafing run. See @{#RANGE.OnAfterStrafeResult}
--
-- # Examples -- # Examples
-- --
-- ## Goldwater Range -- ## Goldwater Range
@@ -366,14 +376,6 @@ RANGE.TargetType = {
COORD = "Coordinate" COORD = "Coordinate"
} }
--- Default range variables for RangeBoss/Hypeman tie in.
hypemanStrafeRollIn = "nil"
StrafeAircraftType = "strafeAircraftTypeNotSet"
Straferesult = {}
clientRollingIn = false
clientStrafed = false
invalidStrafe = false
--- Player settings. --- Player settings.
-- @type RANGE.PlayerData -- @type RANGE.PlayerData
-- @field #boolean smokebombimpact Smoke bomb impact points. -- @field #boolean smokebombimpact Smoke bomb impact points.
@@ -408,6 +410,14 @@ invalidStrafe = false
-- @field #number smokepoints Number of smoke points. -- @field #number smokepoints Number of smoke points.
-- @field #number heading Heading of pit. -- @field #number heading Heading of pit.
--- Strafe status for player.
-- @type RANGE.StrafeStatus
-- @field #number hits Number of hits on target.
-- @field #number time Number of times.
-- @field #number ammo Amount of ammo.
-- @field #boolean pastfoulline If `true`, player passed foul line. Invalid pass.
-- @field #RANGE.StrafeTarget zone Strafe target.
--- Bomb target result. --- Bomb target result.
-- @type RANGE.BombResult -- @type RANGE.BombResult
-- @field #string name Name of closest target. -- @field #string name Name of closest target.
@@ -420,6 +430,13 @@ invalidStrafe = false
-- @field #number time Time via timer.getAbsTime() in seconds of impact. -- @field #number time Time via timer.getAbsTime() in seconds of impact.
-- @field #string date OS date. -- @field #string date OS date.
--- Strafe result.
-- @type RANGE.StrafeResult
-- @field #string player Player name.
-- @field #string airframe Aircraft type of player.
-- @field #number time Time via timer.getAbsTime() in seconds of impact.
-- @field #string date OS date.
--- Sound file data. --- Sound file data.
-- @type RANGE.Soundfile -- @type RANGE.Soundfile
-- @field #string filename Name of the file -- @field #string filename Name of the file
@@ -532,7 +549,7 @@ RANGE.MenuF10Root = nil
--- Range script version. --- Range script version.
-- @field #string version -- @field #string version
RANGE.version = "2.3.0" RANGE.version = "2.4.0"
-- TODO list: -- TODO list:
-- TODO: Verbosity level for messages. -- TODO: Verbosity level for messages.
@@ -583,6 +600,8 @@ function RANGE:New( rangename )
self:AddTransition("Stopped", "Start", "Running") -- Start RANGE script. self:AddTransition("Stopped", "Start", "Running") -- Start RANGE script.
self:AddTransition("*", "Status", "*") -- Status of RANGE script. self:AddTransition("*", "Status", "*") -- Status of RANGE script.
self:AddTransition("*", "Impact", "*") -- Impact of bomb/rocket/missile. self:AddTransition("*", "Impact", "*") -- Impact of bomb/rocket/missile.
self:AddTransition("*", "RollingIn", "*") -- Player rolling in on strafe target.
self:AddTransition("*", "StrafeResult", "*") -- Strafe result of player.
self:AddTransition("*", "EnterRange", "*") -- Player enters the range. self:AddTransition("*", "EnterRange", "*") -- Player enters the range.
self:AddTransition("*", "ExitRange", "*") -- Player leaves the range. self:AddTransition("*", "ExitRange", "*") -- Player leaves the range.
self:AddTransition("*", "Save", "*") -- Save player results. self:AddTransition("*", "Save", "*") -- Save player results.
@@ -640,6 +659,37 @@ function RANGE:New( rangename )
-- @param #RANGE.BombResult result Data of the bombing run. -- @param #RANGE.BombResult result Data of the bombing run.
-- @param #RANGE.PlayerData player Data of player settings etc. -- @param #RANGE.PlayerData player Data of player settings etc.
--- Triggers the FSM event "RollingIn".
-- @function [parent=#RANGE] RollingIn
-- @param #RANGE self
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeTarget target Strafe target.
--- On after "RollingIn" event user function. Called when a player rolls in to a strafe taret.
-- @function [parent=#RANGE] OnAfterRollingIn
-- @param #RANGE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeTarget target Strafe target.
--- Triggers the FSM event "StrafeResult".
-- @function [parent=#RANGE] StrafeResult
-- @param #RANGE self
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeResult result Data of the strafing run.
--- On after "StrafeResult" event user function. Called when a player finished a strafing run.
-- @function [parent=#RANGE] OnAfterStrafeResult
-- @param #RANGE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeResult result Data of the strafing run.
--- Triggers the FSM event "EnterRange". --- Triggers the FSM event "EnterRange".
-- @function [parent=#RANGE] EnterRange -- @function [parent=#RANGE] EnterRange
-- @param #RANGE self -- @param #RANGE self
@@ -1594,7 +1644,6 @@ function RANGE:OnEventBirth( EventData )
self.strafeStatus[_uid] = nil self.strafeStatus[_uid] = nil
-- Add Menu commands after a delay of 0.1 seconds. -- Add Menu commands after a delay of 0.1 seconds.
-- SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName ) self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
-- By default, some bomb impact points and do not flare each hit on target. -- By default, some bomb impact points and do not flare each hit on target.
@@ -1613,7 +1662,6 @@ function RANGE:OnEventBirth( EventData )
-- Start check in zone timer. -- Start check in zone timer.
if self.planes[_uid] ~= true then if self.planes[_uid] ~= true then
-- SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1)
self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 ) self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 )
self.planes[_uid] = true self.planes[_uid] = true
end end
@@ -1647,7 +1695,7 @@ function RANGE:OnEventHit( EventData )
local targetname = EventData.TgtUnitName local targetname = EventData.TgtUnitName
-- Current strafe target of player. -- Current strafe target of player.
local _currentTarget = self.strafeStatus[_unitID] local _currentTarget = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
-- Player has rolled in on a strafing target. -- Player has rolled in on a strafing target.
if _currentTarget and target:IsAlive() then if _currentTarget and target:IsAlive() then
@@ -1931,74 +1979,6 @@ end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions -- FSM Functions
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
function RANGE:_SaveTargetSheet( _playername, result ) -- RangeBoss Specific Function
--- Function that saves data to file
local function _savefile( filename, data )
local f = io.open( filename, "wb" )
if f then
f:write( data )
f:close()
else
env.info( "RANGEBOSS EDIT - could not save target sheet to file" )
-- self:E(self.lid..string.format("ERROR: could not save target sheet to file %s.\nFile may contain invalid characters.", tostring(filename)))
end
end
-- Set path or default.
local path = self.targetpath
if lfs then
path = path or lfs.writedir() .. [[Logs\]]
end
-- Create unused file name.
local filename = nil
for i = 1, 9999 do
-- Create file name
if self.targetprefix then
filename = string.format( "%s_%s-%04d.csv", self.targetprefix, playerData.actype, i )
else
local name = UTILS.ReplaceIllegalCharacters( _playername, "_" )
filename = string.format( "RANGERESULTS-%s_Targetsheet-%s-%04d.csv", self.rangename, name, i )
end
-- Set path.
if path ~= nil then
filename = path .. "\\" .. filename
end
-- Check if file exists.
local _exists = UTILS.FileExists( filename )
if not _exists then
break
end
end
-- Header line
local data = "Name,Target,Distance,Radial,Quality,Rounds Fired,Rounds Hit,Rounds Quality,Attack Heading,Weapon,Airframe,Mission Time,OS Time\n"
-- local result=_result --#RANGE.BombResult
local distance = result.distance
local weapon = result.weapon
local target = result.name
local radial = result.radial
local quality = result.quality
local time = UTILS.SecondsToClock( result.time )
local airframe = result.airframe
local date = "n/a"
local roundsFired = result.roundsFired
local roundsHit = result.roundsHit
local strafeResult = result.roundsQuality
local attackHeading = result.heading
if os then
date = os.date()
end
data = data .. string.format( "%s,%s,%.2f,%03d,%s,%03d,%03d,%s,%03d,%s,%s,%s,%s", _playername, target, distance, radial, quality, roundsFired, roundsHit, strafeResult, attackHeading, weapon, airframe, time, date )
-- Save file.
_savefile( filename, data )
end
--- Check spawn queue and spawn aircraft if necessary. --- Check spawn queue and spawn aircraft if necessary.
-- @param #RANGE self -- @param #RANGE self
@@ -2299,6 +2279,73 @@ function RANGE:onafterLoad( From, Event, To )
end end
end end
--- Save target sheet.
-- @param #RANGE self
-- @param #string _playername Player name.
-- @param #RANGE.StrafeResult result Results table.
function RANGE:_SaveTargetSheet( _playername, result ) -- RangeBoss Specific Function
--- Function that saves data to file
local function _savefile( filename, data )
local f = io.open( filename, "wb" )
if f then
f:write( data )
f:close()
else
env.info( "RANGEBOSS EDIT - could not save target sheet to file" )
-- self:E(self.lid..string.format("ERROR: could not save target sheet to file %s.\nFile may contain invalid characters.", tostring(filename)))
end
end
-- Set path or default.
local path = self.targetpath
if lfs then
path = path or lfs.writedir() .. [[Logs\]]
end
-- Create unused file name.
local filename = nil
for i = 1, 9999 do
-- Create file name
if self.targetprefix then
filename = string.format( "%s_%s-%04d.csv", self.targetprefix, result.airframe, i )
else
local name = UTILS.ReplaceIllegalCharacters( _playername, "_" )
filename = string.format( "RANGERESULTS-%s_Targetsheet-%s-%04d.csv", self.rangename, name, i )
end
-- Set path.
if path ~= nil then
filename = path .. "\\" .. filename
end
-- Check if file exists.
local _exists = UTILS.FileExists( filename )
if not _exists then
break
end
end
-- Header line
local data = "Name,Target,Rounds Fired,Rounds Hit,Rounds Quality,Airframe,Mission Time,OS Time\n"
local target = result.name
local airframe = result.airframe
local roundsFired = result.roundsFired
local roundsHit = result.roundsHit
local strafeResult = result.roundsQuality
local time = UTILS.SecondsToClock( result.time )
local date = "n/a"
if os then
date = os.date()
end
data = data .. string.format( "%s,%s,%d,%d,%s,%s,%s,%s", _playername, target, roundsFired, roundsHit, strafeResult, airframe, time, date )
-- Save file.
_savefile( filename, data )
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Display Messages -- Display Messages
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2344,9 +2391,10 @@ function RANGE:_DisplayMyStrafePitResults( _unitName )
-- Loop over results -- Loop over results
for _, _result in pairs( _results ) do for _, _result in pairs( _results ) do
local result=_result --#RANGE.StrafeResult
-- Message text. -- Message text.
_message = _message .. string.format( "\n[%d] Hits %d - %s - %s", _count, _result.hits, _result.zone.name, _result.text ) _message = _message .. string.format( "\n[%d] Hits %d - %s - %s", _count, result.roundsHit, result.name, result.roundsQuality )
-- Best result. -- Best result.
if _bestMsg == "" then if _bestMsg == "" then
@@ -2836,13 +2884,15 @@ function RANGE:_CheckInZone( _unitName )
if _unit and _playername then if _unit and _playername then
-- Player data.
local playerData=self.PlayerSettings[_playername] -- #RANGE.PlayerData
--- Function to check if unit is in zone and facing in the right direction and is below the max alt. --- Function to check if unit is in zone and facing in the right direction and is below the max alt.
local function checkme( targetheading, _zone ) local function checkme( targetheading, _zone )
local zone = _zone -- Core.Zone#ZONE local zone = _zone -- Core.Zone#ZONE
-- Heading check. -- Heading check.
local unitheading = _unit:GetHeading() local unitheading = _unit:GetHeading()
unitheadingStrafe = _unit:GetHeading() -- RangeBoss
local pitheading = targetheading - 180 local pitheading = targetheading - 180
local deltaheading = unitheading - pitheading local deltaheading = unitheading - pitheading
local towardspit = math.abs( deltaheading ) <= 90 or math.abs( deltaheading - 360 ) <= 90 local towardspit = math.abs( deltaheading ) <= 90 or math.abs( deltaheading - 360 ) <= 90
@@ -2867,7 +2917,7 @@ function RANGE:_CheckInZone( _unitName )
local _unitID = _unit:GetID() local _unitID = _unit:GetID()
-- Currently strafing? (strafeStatus is nil if not) -- Currently strafing? (strafeStatus is nil if not)
local _currentStrafeRun = self.strafeStatus[_unitID] local _currentStrafeRun = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
if _currentStrafeRun then -- player has already registered for a strafing run. if _currentStrafeRun then -- player has already registered for a strafing run.
@@ -2879,7 +2929,6 @@ function RANGE:_CheckInZone( _unitName )
-- Check if player is in strafe zone and below max alt. -- Check if player is in strafe zone and below max alt.
if unitinzone then if unitinzone then
StrafeAircraftType = _unit:GetTypeName() -- RangeBoss
-- Still in zone, keep counting hits. Increase counter. -- Still in zone, keep counting hits. Increase counter.
_currentStrafeRun.time = _currentStrafeRun.time + 1 _currentStrafeRun.time = _currentStrafeRun.time + 1
@@ -2909,8 +2958,10 @@ function RANGE:_CheckInZone( _unitName )
local _ammo = self:_GetAmmo( _unitName ) local _ammo = self:_GetAmmo( _unitName )
-- Result. -- Result.
local _result = self.strafeStatus[_unitID] local _result = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
local _sound = nil -- #RANGE.Soundfile local _sound = nil -- #RANGE.Soundfile
--[[ --RangeBoss commented out in order to implement strafe quality based on accuracy percentage, not the number of rounds on target --[[ --RangeBoss commented out in order to implement strafe quality based on accuracy percentage, not the number of rounds on target
-- Judge this pass. Text is displayed on summary. -- Judge this pass. Text is displayed on summary.
if _result.hits >= _result.zone.goodPass*2 then if _result.hits >= _result.zone.goodPass*2 then
@@ -2927,6 +2978,7 @@ function RANGE:_CheckInZone( _unitName )
_sound=RANGE.Sound.RCPoorPass _sound=RANGE.Sound.RCPoorPass
end end
]] ]]
-- Calculate accuracy of run. Number of hits wrt number of rounds fired. -- Calculate accuracy of run. Number of hits wrt number of rounds fired.
local shots = _result.ammo - _ammo local shots = _result.ammo - _ammo
local accur = 0 local accur = 0
@@ -2937,28 +2989,29 @@ function RANGE:_CheckInZone( _unitName )
end end
end end
if invalidStrafe == true then -- -- Results text and sound message.
_result.text = "* INVALID - PASSED FOUL LINE *" local resulttext=""
if _result.pastfoulline == true then --
resulttext = "* INVALID - PASSED FOUL LINE *"
_sound = RANGE.Sound.RCPoorPass -- _sound = RANGE.Sound.RCPoorPass --
else else
if accur >= 90 then if accur >= 90 then
_result.text = "DEADEYE PASS" resulttext = "DEADEYE PASS"
_sound = RANGE.Sound.RCExcellentPass _sound = RANGE.Sound.RCExcellentPass
elseif accur >= 75 then elseif accur >= 75 then
_result.text = "EXCELLENT PASS" resulttext = "EXCELLENT PASS"
_sound = RANGE.Sound.RCExcellentPass _sound = RANGE.Sound.RCExcellentPass
elseif accur >= 50 then elseif accur >= 50 then
_result.text = "GOOD PASS" resulttext = "GOOD PASS"
_sound = RANGE.Sound.RCGoodPass _sound = RANGE.Sound.RCGoodPass
elseif accur >= 25 then elseif accur >= 25 then
_result.text = "INEFFECTIVE PASS" resulttext = "INEFFECTIVE PASS"
_sound = RANGE.Sound.RCIneffectivePass _sound = RANGE.Sound.RCIneffectivePass
else else
_result.text = "POOR PASS" resulttext = "POOR PASS"
_sound = RANGE.Sound.RCPoorPass _sound = RANGE.Sound.RCPoorPass
end end
end end
clientStrafed = true -- RANGEBOSS
-- Message text. -- Message text.
local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits ) local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits )
@@ -2970,44 +3023,26 @@ function RANGE:_CheckInZone( _unitName )
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _text ) self:_DisplayMessageToGroup( _unit, _text )
-- RangeBoss Edit for strafe table insert -- Strafe result.
local result = {} -- #RANGE.StrafeResult
-- Local results. result.player=_playername
result.name=_result.zone.name or "unknown"
local result = {} -- #RANGE.BombResult
result.name = _result.zone.name or "unknown"
result.distance = 0
result.radial = 0
result.weapon = "N/A"
result.quality = "N/A"
result.player = _playernamee
result.time = timer.getAbsTime() result.time = timer.getAbsTime()
result.airframe = StrafeAircraftType result.roundsFired = shots
result.roundsFired = shots -- RANGEBOSS result.roundsHit = _result.hits
result.roundsHit = _result.hits -- RANGEBOSS result.roundsQuality = resulttext
result.roundsQuality = _result.text -- RANGEBOSS
result.strafeAccuracy = accur result.strafeAccuracy = accur
result.heading = unitheadingStrafe -- RANGEBOSS result.rangename = self.rangename
result.airframe=playerData.airframe
result.invalid = _result.pastfoulline
Straferesult.name = _result.zone.name or "unknown" -- Griger Results.
Straferesult.distance = 0 self:StrafeResult(playerData, result)
Straferesult.radial = 0
Straferesult.weapon = "N/A"
Straferesult.quality = "N/A"
Straferesult.player = _playername
Straferesult.time = timer.getAbsTime()
Straferesult.airframe = StrafeAircraftType
Straferesult.roundsFired = shots
Straferesult.roundsHit = _result.hits
Straferesult.roundsQuality = _result.text
Straferesult.strafeAccuracy = accur
Straferesult.rangename = self.rangename
-- Save trap sheet. -- Save trap sheet.
if playerData and playerData.targeton and self.targetsheet then if playerData and playerData.targeton and self.targetsheet then
self:_SaveTargetSheet( _playername, result ) self:_SaveTargetSheet( _playername, result )
end end
-- RangeBoss edit for strafe data saved to file
-- Voice over. -- Voice over.
if self.rangecontrol then if self.rangecontrol then
@@ -3028,7 +3063,7 @@ function RANGE:_CheckInZone( _unitName )
-- Save stats so the player can retrieve them. -- Save stats so the player can retrieve them.
local _stats = self.strafePlayerResults[_playername] or {} local _stats = self.strafePlayerResults[_playername] or {}
table.insert( _stats, _result ) table.insert( _stats, result )
self.strafePlayerResults[_playername] = _stats self.strafePlayerResults[_playername] = _stats
end end
@@ -3038,12 +3073,13 @@ function RANGE:_CheckInZone( _unitName )
-- Check to see if we're in any of the strafing zones (first time). -- Check to see if we're in any of the strafing zones (first time).
for _, _targetZone in pairs( self.strafeTargets ) do for _, _targetZone in pairs( self.strafeTargets ) do
local target=_targetZone --#RANGE.StrafeTarget
-- Get the current approach zone and check if player is inside. -- Get the current approach zone and check if player is inside.
local zone = _targetZone.polygon -- Core.Zone#ZONE_POLYGON_BASE local zone = target.polygon -- Core.Zone#ZONE_POLYGON_BASE
-- Check if unit in zone and facing the right direction. -- Check if unit in zone and facing the right direction.
local unitinzone = checkme( _targetZone.heading, zone ) local unitinzone = checkme( target.heading, zone )
-- Player is inside zone. -- Player is inside zone.
if unitinzone then if unitinzone then
@@ -3052,19 +3088,20 @@ function RANGE:_CheckInZone( _unitName )
local _ammo = self:_GetAmmo( _unitName ) local _ammo = self:_GetAmmo( _unitName )
-- Init strafe status for this player. -- Init strafe status for this player.
self.strafeStatus[_unitID] = { hits = 0, zone = _targetZone, time = 1, ammo = _ammo, pastfoulline = false } self.strafeStatus[_unitID] = { hits = 0, zone = target, time = 1, ammo = _ammo, pastfoulline = false }
-- Rolling in! -- Rolling in!
local _msg = string.format( "%s, rolling in on strafe pit %s.", self:_myname( _unitName ), _targetZone.name ) local _msg = string.format( "%s, rolling in on strafe pit %s.", self:_myname( _unitName ), target.name )
if self.rangecontrol then if self.rangecontrol then
self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath ) self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath )
end end
clientRollingIn = true -- RANGEBOSS
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _msg, 10, true ) self:_DisplayMessageToGroup( _unit, _msg, 10, true )
hypemanStrafeRollIn = _msg -- RANGEBOSS
-- Trigger event that player is rolling in.
self:RollingIn(playerData, target)
-- We found our player. Skip remaining checks. -- We found our player. Skip remaining checks.
break break

View File

@@ -11210,7 +11210,7 @@ end
--- Get wind direction and speed at carrier position. --- Get wind direction and speed at carrier position.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #number alt Altitude ASL in meters. Default 50 m. -- @param #number alt Altitude ASL in meters. Default 15 m.
-- @param #boolean magnetic Direction including magnetic declination. -- @param #boolean magnetic Direction including magnetic declination.
-- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position. -- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position.
-- @return #number Direction the wind is blowing **from** in degrees. -- @return #number Direction the wind is blowing **from** in degrees.
@@ -11220,8 +11220,8 @@ function AIRBOSS:GetWind( alt, magnetic, coord )
-- Current position of the carrier or input. -- Current position of the carrier or input.
local cv = coord or self:GetCoordinate() local cv = coord or self:GetCoordinate()
-- Wind direction and speed. By default at 50 meters ASL. -- Wind direction and speed. By default at 15 meters ASL.
local Wdir, Wspeed = cv:GetWind( alt or 50 ) local Wdir, Wspeed = cv:GetWind( alt or 15 )
-- Include magnetic declination. -- Include magnetic declination.
if magnetic then if magnetic then

View File

@@ -30,7 +30,7 @@
-- @module Ops.CSAR -- @module Ops.CSAR
-- @image OPS_CSAR.jpg -- @image OPS_CSAR.jpg
-- Date: Feb 2022 -- Date: June 2022
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@@ -76,65 +76,69 @@
-- --
-- The following options are available (with their defaults). Only set the ones you want changed: -- The following options are available (with their defaults). Only set the ones you want changed:
-- --
-- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms. -- mycsar.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! -- mycsar.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. -- mycsar.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. -- mycsar.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
-- self.autosmokedistance = 1000 -- distance for autosmoke -- mycsar.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. -- mycsar.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
-- self.csarOncrash = false -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well. -- mycsar.csarOncrash = false -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well.
-- self.enableForAI = false -- set to false to disable AI pilots from being rescued. -- mycsar.enableForAI = false -- set to false to disable AI pilots from being rescued.
-- self.pilotRuntoExtractPoint = true -- Downed pilot will run to the rescue helicopter up to self.extractDistance in meters. -- mycsar.pilotRuntoExtractPoint = true -- Downed pilot will run to the rescue helicopter up to mycsar.extractDistance in meters.
-- self.extractDistance = 500 -- Distance the downed pilot will start to run to the rescue helicopter. -- mycsar.extractDistance = 500 -- Distance the downed pilot will start to run to the rescue helicopter.
-- self.immortalcrew = true -- Set to true to make wounded crew immortal. -- mycsar.immortalcrew = true -- Set to true to make wounded crew immortal.
-- self.invisiblecrew = false -- Set to true to make wounded crew insvisible. -- mycsar.invisiblecrew = false -- Set to true to make wounded crew insvisible.
-- self.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters. -- mycsar.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters.
-- self.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes. -- mycsar.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes.
-- self.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined. -- mycsar.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined.
-- self.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages. -- mycsar.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages.
-- self.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons. -- mycsar.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons.
-- self.smokecolor = 4 -- Color of smokemarker, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue. -- mycsar.smokecolor = 4 -- Color of smokemarker, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue.
-- self.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below. -- mycsar.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below.
-- self.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor! -- mycsar.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor!
-- self.verbose = 0 -- set to > 1 for stats output for debugging. -- mycsar.verbose = 0 -- set to > 1 for stats output for debugging.
-- -- (added 0.1.4) limit amount of downed pilots spawned by **ejection** events -- -- (added 0.1.4) limit amount of downed pilots spawned by **ejection** events
-- self.limitmaxdownedpilots = true -- mycsar.limitmaxdownedpilots = true
-- self.maxdownedpilots = 10 -- mycsar.maxdownedpilots = 10
-- -- (added 0.1.8) - allow to set far/near distance for approach and optionally pilot must open doors -- -- (added 0.1.8) - allow to set far/near distance for approach and optionally pilot must open doors
-- self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters -- mycsar.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
-- self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters -- mycsar.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
-- self.pilotmustopendoors = false -- switch to true to enable check of open doors -- mycsar.pilotmustopendoors = false -- switch to true to enable check of open doors
-- -- (added 0.1.9) -- -- (added 0.1.9)
-- self.suppressmessages = false -- switch off all messaging if you want to do your own -- mycsar.suppressmessages = false -- switch off all messaging if you want to do your own
-- -- (added 0.1.11) -- -- (added 0.1.11)
-- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters -- mycsar.rescuehoverheight = 20 -- max height for a hovering rescue in meters
-- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters -- mycsar.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters
-- -- (added 0.1.12) -- -- (added 0.1.12)
-- -- Country codes for spawned pilots -- -- Country codes for spawned pilots
-- self.countryblue= country.id.USA -- mycsar.countryblue= country.id.USA
-- self.countryred = country.id.RUSSIA -- mycsar.countryred = country.id.RUSSIA
-- self.countryneutral = country.id.UN_PEACEKEEPERS -- mycsar.countryneutral = country.id.UN_PEACEKEEPERS
-- --
-- ## 2.1 Experimental Features -- ## 2.1 Experimental Features
-- --
-- WARNING - Here\'ll be dragons! -- WARNING - Here\'ll be dragons!
-- DANGER - For this to work you need to de-sanitize your mission environment (all three entries) in <DCS root>\Scripts\MissionScripting.lua -- DANGER - For this to work you need to de-sanitize your mission environment (all three entries) in <DCS root>\Scripts\MissionScripting.lua
-- Needs SRS => 1.9.6 to work (works on the **server** side of SRS) -- Needs SRS => 1.9.6 to work (works on the **server** side of SRS)
-- self.useSRS = false -- Set true to use FF\'s SRS integration -- mycsar.useSRS = false -- Set true to use FF\'s SRS integration
-- self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!) -- mycsar.SRSPath = "C:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!)
-- self.SRSchannel = 300 -- radio channel -- mycsar.SRSchannel = 300 -- radio channel
-- self.SRSModulation = radio.modulation.AM -- modulation -- mycsar.SRSModulation = radio.modulation.AM -- modulation
-- self.SRSport = 5002 -- and SRS port -- mycsar.SRSport = 5002 -- and SRS Server port
-- mycsar.SRSCulture = "en-GB" -- SRS voice culture
-- mycsar.SRSVoice = nil -- SRS voice, relevant for Google TTS
-- mycsar.SRSGPathToCredentials = nil -- Path to your Google credentials json file, set this if you want to use Google TTS
-- mycsar.SRSVolume = 1 -- Volume, between 0 and 1
-- -- -- --
-- self.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection --shagrat -- mycsar.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection. Requires mycsar.enableForAI to be set to true. --shagrat
-- self.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases. -- mycsar.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
-- --
-- ## 3. Results -- ## 3. Results
-- --
-- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object: -- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object:
-- --
-- self.rescues -- number of successful landings *with* saved pilots -- mycsar.rescues -- number of successful landings *with* saved pilots
-- self.rescuedpilots -- aggregated number of pilots rescued from the field (of *all* players) -- mycsar.rescuedpilots -- aggregated number of pilots rescued from the field (of *all* players)
-- --
-- ## 4. Events -- ## 4. Events
-- --
@@ -260,7 +264,7 @@ CSAR.AircraftType["AH-64D_BLK_II"] = 2
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version="1.0.4e" CSAR.version="1.0.5"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@@ -415,6 +419,10 @@ function CSAR:New(Coalition, Template, Alias)
self.SRSchannel = 300 -- radio channel self.SRSchannel = 300 -- radio channel
self.SRSModulation = radio.modulation.AM -- modulation self.SRSModulation = radio.modulation.AM -- modulation
self.SRSport = 5002 -- port self.SRSport = 5002 -- port
self.SRSCulture = "en-GB"
self.SRSVoice = nil
self.SRSGPathToCredentials = nil
self.SRSVolume = 1
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
@@ -861,12 +869,12 @@ function CSAR:_EventHandler(EventData)
-- no Player -- no Player
if self.enableForAI == false and _event.IniPlayerName == nil then if self.enableForAI == false and _event.IniPlayerName == nil then
return return self
end end
-- no event -- no event
if _event == nil or _event.initiator == nil then if _event == nil or _event.initiator == nil then
return false return self
-- take off -- take off
elseif _event.id == EVENTS.Takeoff then -- taken off elseif _event.id == EVENTS.Takeoff then -- taken off
@@ -874,35 +882,43 @@ function CSAR:_EventHandler(EventData)
local _coalition = _event.IniCoalition local _coalition = _event.IniCoalition
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! return self --ignore!
end end
if _event.IniGroupName then if _event.IniGroupName then
self.takenOff[_event.IniUnitName] = true self.takenOff[_event.IniUnitName] = true
end end
return true return self
-- player enter unit -- player enter unit
elseif _event.id == EVENTS.PlayerEnterAircraft or _event.id == EVENTS.PlayerEnterUnit then --player entered unit elseif _event.id == EVENTS.PlayerEnterAircraft or _event.id == EVENTS.PlayerEnterUnit then --player entered unit
self:T(self.lid .. " Event unit - Player Enter") self:T(self.lid .. " Event unit - Player Enter")
local _coalition = _event.IniCoalition local _coalition = _event.IniCoalition
self:T("Coalition = "..UTILS.GetCoalitionName(_coalition))
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! return self --ignore!
end end
if _event.IniPlayerName then if _event.IniPlayerName then
self.takenOff[_event.IniPlayerName] = nil self.takenOff[_event.IniPlayerName] = nil
end end
-- jumped into flying plane?
self:T("Taken Off: "..tostring(_event.IniUnit:InAir(true)))
if _event.IniUnit:InAir(true) then
self.takenOff[_event.IniPlayerName] = true
end
local _unit = _event.IniUnit local _unit = _event.IniUnit
local _group = _event.IniGroup local _group = _event.IniGroup
if _unit:IsHelicopter() or _group:IsHelicopter() then if _unit:IsHelicopter() or _group:IsHelicopter() then
self:_AddMedevacMenuItem() self:_AddMedevacMenuItem()
end end
return true return self
elseif (_event.id == EVENTS.PilotDead and self.csarOncrash == false) then elseif (_event.id == EVENTS.PilotDead and self.csarOncrash == false) then
-- Pilot dead -- Pilot dead
@@ -914,29 +930,29 @@ function CSAR:_EventHandler(EventData)
local _group = _event.IniGroup local _group = _event.IniGroup
if _unit == nil then if _unit == nil then
return -- error! return self -- error!
end end
local _coalition = _event.IniCoalition local _coalition = _event.IniCoalition
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! return self --ignore!
end end
-- Catch multiple events here? -- Catch multiple events here?
if self.takenOff[_event.IniUnitName] == true or _group:IsAirborne() then if self.takenOff[_event.IniUnitName] == true or _group:IsAirborne() then
if self:_DoubleEjection(_unitname) then if self:_DoubleEjection(_unitname) then
return return self
end end
else else
self:T(self.lid .. " Pilot has not taken off, ignore") self:T(self.lid .. " Pilot has not taken off, ignore")
end end
return return self
elseif _event.id == EVENTS.PilotDead or _event.id == EVENTS.Ejection then elseif _event.id == EVENTS.PilotDead or _event.id == EVENTS.Ejection then
if _event.id == EVENTS.PilotDead and self.csarOncrash == false then if _event.id == EVENTS.PilotDead and self.csarOncrash == false then
return return self
end end
self:T(self.lid .. " Event unit - Pilot Ejected") self:T(self.lid .. " Event unit - Pilot Ejected")
@@ -944,27 +960,38 @@ function CSAR:_EventHandler(EventData)
local _unitname = _event.IniUnitName local _unitname = _event.IniUnitName
local _group = _event.IniGroup local _group = _event.IniGroup
self:T({_unit.UnitName, _unitname, _group.GroupName})
if _unit == nil then if _unit == nil then
return -- error! self:T("Unit NIL!")
return self -- error!
end end
local _coalition = _unit:GetCoalition() --local _coalition = _unit:GetCoalition() -- nil now for some reason
local _coalition = _group:GetCoalition()
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! self:T("Wrong coalition! Coalition = "..UTILS.GetCoalitionName(_coalition))
return self --ignore!
end end
self:T("Airborne: "..tostring(_group:IsAirborne()))
self:T("Taken Off: "..tostring(self.takenOff[_event.IniUnitName]))
if not self.takenOff[_event.IniUnitName] and not _group:IsAirborne() then if not self.takenOff[_event.IniUnitName] and not _group:IsAirborne() then
self:T(self.lid .. " Pilot has not taken off, ignore") self:T(self.lid .. " Pilot has not taken off, ignore")
return -- give up, pilot hasnt taken off -- return self -- give up, pilot hasnt taken off
end end
if self:_DoubleEjection(_unitname) then if self:_DoubleEjection(_unitname) then
return self:T("Double Ejection!")
return self
end end
-- limit no of pilots in the field. -- limit no of pilots in the field.
if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then
return self:T("Maxed Downed Pilot!")
return self
end end
@@ -973,33 +1000,27 @@ function CSAR:_EventHandler(EventData)
local wetfeet = false local wetfeet = false
local surface = _unit:GetCoordinate():GetSurfaceType() local initdcscoord = nil
local initcoord = nil
--if _event.id == EVENTS.Ejection then
initdcscoord = _event.TgtDCSUnit:getPoint()
initcoord = COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord})
--end
--local surface = _unit:GetCoordinate():GetSurfaceType()
local surface = initcoord:GetSurfaceType()
if surface == land.SurfaceType.WATER then if surface == land.SurfaceType.WATER then
self:T("Wet feet!")
wetfeet = true wetfeet = true
end end
-- all checks passed, get going. -- all checks passed, get going.
if self.csarUsePara == false or (self.csarUsePara and wetfeet ) then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land if self.csarUsePara == false or (self.csarUsePara and wetfeet ) then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land
local _freq = self:_GenerateADFFrequency()
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
return true
end
---- shagrat on event LANDING_AFTER_EJECTION spawn pilot at parachute location
elseif (_event.id == EVENTS.LandingAfterEjection and self.csarUsePara == true) then
self:I({EVENT=_event})
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
local _unitname = "Aircraft" --_event.initiator:getName() or "Aircraft" --shagrat Optional use of Object name which is unfortunately 'f15_Pilot_Parachute'
local _typename = "Ejected Pilot" --_event.Initiator.getTypeName() or "Ejected Pilot"
local _country = _event.initiator:getCountry()
local _coalition = coalition.getCountryCoalition( _country )
if _coalition == self.coalition then
local _freq = self:_GenerateADFFrequency() local _freq = self:_GenerateADFFrequency()
self:I({coalition=_coalition,country= _country, coord=_LandingPos, name=_unitname, player=_event.IniPlayerName, freq=_freq}) self:_AddCsar(_coalition, _unit:GetCountry(), initcoord , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
self:_AddCsar(_coalition, _country, _LandingPos, nil, _unitname, _event.IniPlayerName, _freq, false, "none")--shagrat add CSAR at Parachute location. return self
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
end end
return true
elseif _event.id == EVENTS.Land then elseif _event.id == EVENTS.Land then
self:T(self.lid .. " Landing") self:T(self.lid .. " Landing")
@@ -1014,12 +1035,12 @@ function CSAR:_EventHandler(EventData)
if _unit == nil then if _unit == nil then
self:T(self.lid .. " Unit nil on landing") self:T(self.lid .. " Unit nil on landing")
return -- error! return self -- error!
end end
local _coalition = _event.IniCoalition local _coalition = _event.IniCoalition
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! return self --ignore!
end end
self.takenOff[_event.IniUnitName] = nil self.takenOff[_event.IniUnitName] = nil
@@ -1028,13 +1049,13 @@ function CSAR:_EventHandler(EventData)
if _place == nil then if _place == nil then
self:T(self.lid .. " Landing Place Nil") self:T(self.lid .. " Landing Place Nil")
return -- error! return self -- error!
end end
-- anyone on board? -- anyone on board?
if self.inTransitGroups[_event.IniUnitName] == nil then if self.inTransitGroups[_event.IniUnitName] == nil then
-- ignore -- ignore
return return self
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
@@ -1044,8 +1065,27 @@ function CSAR:_EventHandler(EventData)
end end
end end
return true return self
end end
---- shagrat on event LANDING_AFTER_EJECTION spawn pilot at parachute location
if (_event.id == EVENTS.LandingAfterEjection and self.csarUsePara == true) then
self:T("LANDING_AFTER_EJECTION")
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
local _unitname = "Aircraft" --_event.initiator:getName() or "Aircraft" --shagrat Optional use of Object name which is unfortunately 'f15_Pilot_Parachute'
local _typename = "Ejected Pilot" --_event.Initiator.getTypeName() or "Ejected Pilot"
local _country = _event.initiator:getCountry()
local _coalition = coalition.getCountryCoalition( _country )
self:T("Country = ".._country.." Coalition = ".._coalition)
if _coalition == self.coalition then
local _freq = self:_GenerateADFFrequency()
self:I({coalition=_coalition,country= _country, coord=_LandingPos, name=_unitname, player=_event.IniPlayerName, freq=_freq})
self:_AddCsar(_coalition, _country, _LandingPos, nil, _unitname, _event.IniPlayerName, _freq, false, "none")--shagrat add CSAR at Parachute location.
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
end
end
return self return self
end end
@@ -1240,7 +1280,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
_maxUnits = self.max_units _maxUnits = self.max_units
end end
if _unitsInHelicopter + 1 > _maxUnits then if _unitsInHelicopter + 1 > _maxUnits then
self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We\'re already crammed with %d guys! Sorry!", _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), self.messageTime) self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We\'re already crammed with %d guys! Sorry!", _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), self.messageTime,false,false,true)
return true return true
end end
@@ -1329,6 +1369,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
if _time == nil then if _time == nil then
self.landedStatus[_lookupKeyHeli] = math.floor( (_distance - self.loadDistance) / 3.6 ) self.landedStatus[_lookupKeyHeli] = math.floor( (_distance - self.loadDistance) / 3.6 )
_time = self.landedStatus[_lookupKeyHeli] _time = self.landedStatus[_lookupKeyHeli]
_woundedGroup:OptionAlarmStateGreen()
self:_OrderGroupToMoveToPoint(_woundedGroup, _heliUnit:GetCoordinate()) self:_OrderGroupToMoveToPoint(_woundedGroup, _heliUnit:GetCoordinate())
self:_DisplayMessageToSAR(_heliUnit, "Wait till " .. _pilotName .. " gets in. \nETA " .. _time .. " more seconds.", self.messageTime, false) self:_DisplayMessageToSAR(_heliUnit, "Wait till " .. _pilotName .. " gets in. \nETA " .. _time .. " more seconds.", self.messageTime, false)
else else
@@ -1338,7 +1379,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
--if _time <= 0 or _distance < self.loadDistance then --if _time <= 0 or _distance < self.loadDistance then
if _distance < self.loadDistance + 5 or _distance <= 13 then if _distance < self.loadDistance + 5 or _distance <= 13 then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true)
return true return true
else else
self.landedStatus[_lookupKeyHeli] = nil self.landedStatus[_lookupKeyHeli] = nil
@@ -1350,7 +1391,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
else else
if (_distance < self.loadDistance) then if (_distance < self.loadDistance) then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true)
return true return true
else else
self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName)
@@ -1392,7 +1433,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true)
else else
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true)
return true return true
else else
self.hoverStatus[_lookupKeyHeli] = nil self.hoverStatus[_lookupKeyHeli] = nil
@@ -1451,7 +1492,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname, isairport)
if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false 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, true)
else else
self:_RescuePilots(_heliUnit) self:_RescuePilots(_heliUnit)
return return
@@ -1526,6 +1567,14 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _overrid
local channel = self.SRSchannel local channel = self.SRSchannel
local msrs = MSRS:New(path,channel,modulation) local msrs = MSRS:New(path,channel,modulation)
msrs:SetPort(self.SRSport) msrs:SetPort(self.SRSport)
msrs:SetLabel("CSAR")
msrs:SetCulture(self.SRSCulture)
msrs:SetCoalition(self.coalition)
msrs:SetVoice(self.SRSVoice)
if self.SRSGPathToCredentials then
msrs:SetGoogle(self.SRSGPathToCredentials)
end
msrs:SetVolume(self.SRSVolume)
msrs:PlaySoundText(srstext, 2) msrs:PlaySoundText(srstext, 2)
end end
return self return self

View File

@@ -723,6 +723,7 @@ do
-- ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700}, -- ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700},
-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000},
-- ["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- ["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500},
-- ["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200},
-- --
-- ### 2.1.2 Activate and deactivate zones -- ### 2.1.2 Activate and deactivate zones
-- --
@@ -870,12 +871,13 @@ do
-- --
-- ## 5. Support for Hercules mod by Anubis -- ## 5. Support for Hercules mod by Anubis
-- --
-- Basic support for the Hercules mod By Anubis has been build into CTLD - that is you can load/drop/build the same objects as the helicopters. -- Basic support for the Hercules mod By Anubis has been build into CTLD - that is you can load/drop/build the same way and for the same objects as
-- To also cover objects and troops which can be loaded from the groud crew Rearm/Refuel menu, you need to use @{#CTLD_HERCULES.New}() and link -- the helicopters (main method).
-- this object to your CTLD setup. In this case, do **not** use the `Hercules_Cargo.lua` or `Hercules_Cargo_CTLD.lua` which are part of the mod -- To cover objects and troops which can be loaded from the groud crew Rearm/Refuel menu (F8), you need to use @{#CTLD_HERCULES.New}() and link
-- this object to your CTLD setup (alternative method). In this case, do **not** use the `Hercules_Cargo.lua` or `Hercules_Cargo_CTLD.lua` which are part of the mod
-- in your mission! -- in your mission!
-- --
-- ### 5.1 Create an own CTLD instance and allow the usage of the Hercules mod: -- ### 5.1 Create an own CTLD instance and allow the usage of the Hercules mod (main method)
-- --
-- local my_ctld = CTLD:New(coalition.side.BLUE,{"Helicargo", "Hercules"},"Lufttransportbrigade I") -- local my_ctld = CTLD:New(coalition.side.BLUE,{"Helicargo", "Hercules"},"Lufttransportbrigade I")
-- --
@@ -893,10 +895,11 @@ do
-- --
-- my_ctld.useprefix = true -- this is true by default and MUST BE ON. -- my_ctld.useprefix = true -- this is true by default and MUST BE ON.
-- --
-- ### 5.2 Integrate Hercules ground crew loadable objects -- ### 5.2 Integrate Hercules ground crew (F8 Menu) loadable objects (alternative method)
-- --
-- Add ground crew loadable objects to your CTLD instance like so, where `my_ctld` is the previously created CTLD instance: -- Integrate to your CTLD instance like so, where `my_ctld` is a previously created CTLD instance:
-- --
-- my_ctld.enableHercules = false -- avoid dual loading via CTLD F10 and F8 ground crew
-- local herccargo = CTLD_HERCULES:New("blue", "Hercules Test", my_ctld) -- local herccargo = CTLD_HERCULES:New("blue", "Hercules Test", my_ctld)
-- --
-- You also need: -- You also need:
@@ -906,6 +909,7 @@ do
-- --
-- There's a **quick check output in the `dcs.log`** which tells you what's there and what not. -- There's a **quick check output in the `dcs.log`** which tells you what's there and what not.
-- E.g.: -- E.g.:
--
-- ...Checking template for APC BTR-82A Air [24998lb] (BTR-82A) ... MISSING) -- ...Checking template for APC BTR-82A Air [24998lb] (BTR-82A) ... MISSING)
-- ...Checking template for ART 2S9 NONA Skid [19030lb] (SAU 2-C9) ... MISSING) -- ...Checking template for ART 2S9 NONA Skid [19030lb] (SAU 2-C9) ... MISSING)
-- ...Checking template for EWR SBORKA Air [21624lb] (Dog Ear radar) ... MISSING) -- ...Checking template for EWR SBORKA Air [21624lb] (Dog Ear radar) ... MISSING)
@@ -913,6 +917,16 @@ do
-- --
-- Expected template names are the ones in the rounded brackets. -- Expected template names are the ones in the rounded brackets.
-- --
-- ### 5.2.1 Hints
--
-- The script works on the EVENTS.Shot trigger, which is used by the mod when you **drop cargo from the Hercules while flying**. Unloading on the ground does
-- not achieve anything here. If you just want to unload on the ground, use the normal Moose CTLD (see 5.1).
--
-- There are two ways of airdropping:
--
-- 1) Very low and very slow (>5m and <10m AGL) - here you can drop stuff which has "Skid" at the end of the cargo name (loaded via F8 Ground Crew menu)
-- 2) Higher up and slow (>100m AGL) - here you can drop paratroopers and cargo which has "Air" at the end of the cargo name (loaded via F8 Ground Crew menu)
--
-- Standard transport capabilities as per the real Hercules are: -- Standard transport capabilities as per the real Hercules are:
-- --
-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers -- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers
@@ -4792,10 +4806,11 @@ end
end -- end do end -- end do
do do
--- Hercules Cargo Drop Events by Anubis Yinepu --- **Hercules Cargo AIR Drop Events** by Anubis Yinepu
-- Moose CTLD OO refactoring by Applevangelist -- Moose CTLD OO refactoring by Applevangelist
-- --
-- This script will only work for the Herculus mod by Anubis -- This script will only work for the Herculus mod by Anubis, and only for **Air Dropping** cargo from the Hercules.
-- Use the standard Moose CTLD if you want to unload on the ground.
-- Payloads carried by pylons 11, 12 and 13 need to be declared in the Herculus_Loadout.lua file -- Payloads carried by pylons 11, 12 and 13 need to be declared in the Herculus_Loadout.lua file
-- Except for Ammo pallets, this script will spawn whatever payload gets launched from pylons 11, 12 and 13 -- Except for Ammo pallets, this script will spawn whatever payload gets launched from pylons 11, 12 and 13
-- Pylons 11, 12 and 13 are moveable within the Herculus cargobay area -- Pylons 11, 12 and 13 are moveable within the Herculus cargobay area
@@ -4883,7 +4898,7 @@ CTLD_HERCULES.Types = {
["ART GVOZDIKA [34720lb]"] = {['name'] = "SAU Gvozdika", ['container'] = false}, ["ART GVOZDIKA [34720lb]"] = {['name'] = "SAU Gvozdika", ['container'] = false},
["APC MTLB Air [26400lb]"] = {['name'] = "MTLB", ['container'] = true}, ["APC MTLB Air [26400lb]"] = {['name'] = "MTLB", ['container'] = true},
["APC MTLB Skid [26290lb]"] = {['name'] = "MTLB", ['container'] = false}, ["APC MTLB Skid [26290lb]"] = {['name'] = "MTLB", ['container'] = false},
["Generic Crate [20000lb]"] = {['name'] = "Hercules_Container_Parachute", ['container'] = true} --nothing generic in Moose CTLD --["Generic Crate [20000lb]"] = {['name'] = "Hercules_Container_Parachute", ['container'] = true} --nothing generic in Moose CTLD
} }
--- Cargo Object --- Cargo Object
@@ -4909,6 +4924,7 @@ CTLD_HERCULES.Types = {
-- @usage -- @usage
-- Integrate to your CTLD instance like so, where `my_ctld` is a previously created CTLD instance: -- Integrate to your CTLD instance like so, where `my_ctld` is a previously created CTLD instance:
-- --
-- my_ctld.enableHercules = false -- avoid dual loading via CTLD F10 and F8 ground crew
-- local herccargo = CTLD_HERCULES:New("blue", "Hercules Test", my_ctld) -- local herccargo = CTLD_HERCULES:New("blue", "Hercules Test", my_ctld)
-- --
-- You also need: -- You also need:
@@ -4922,6 +4938,14 @@ CTLD_HERCULES.Types = {
-- ...Checking template for Transport Tigr Air [15900lb] (Tigr_233036) ... OK) -- ...Checking template for Transport Tigr Air [15900lb] (Tigr_233036) ... OK)
-- --
-- Expected template names are the ones in the rounded brackets. -- Expected template names are the ones in the rounded brackets.
--
-- HINTS
--
-- The script works on the EVENTS.Shot trigger, which is used by the mod when you **drop cargo from the Hercules while flying**. Unloading on the ground does
-- not achieve anything here. If you just want to unload on the ground, use the normal Moose CTLD.
-- There are two ways of airdropping:
-- 1) Very low and very slow (>5m and <10m AGL) - here you can drop stuff which has "Skid" at the end of the cargo name (loaded via F8 Ground Crew menu)
-- 2) Higher up and slow (>100m AGL) - here you can drop paratroopers and cargo which has "Air" at the end of the cargo name (loaded via F8 Ground Crew menu)
function CTLD_HERCULES:New(Coalition, Alias, CtldObject) function CTLD_HERCULES:New(Coalition, Alias, CtldObject)
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #CTLD_HERCULES local self=BASE:Inherit(self, FSM:New()) -- #CTLD_HERCULES

View File

@@ -96,6 +96,14 @@
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech -- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml -- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
-- --
-- **NOTE on using GOOGLE TTS with SRS:** You need to have the C# library installed in your SRS folder for Google to work.
-- You can obtain it e.g. here: [NuGet](https://www.nuget.org/packages/Grpc.Core)
--
-- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing.
-- and also the Google Console error, in case you have missed a step in setting up your Google TTS.
-- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"`
-- Plays a message on 255AM for the blue coalition in-game.
--
-- ## Set Voice -- ## Set Voice
-- --
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
@@ -111,6 +119,10 @@
-- --
-- Use @{#MSRS.SetPort} to define the SRS port. Defaults to 5002. -- Use @{#MSRS.SetPort} to define the SRS port. Defaults to 5002.
-- --
-- ## Set SRS Volume
--
-- Use @{#MSRS.SetVolume} to define the SRS volume. Defaults to 1.0. Allowed values are between 0.0 and 1.0, from silent to loudest.
--
-- @field #MSRS -- @field #MSRS
MSRS = { MSRS = {
ClassName = "MSRS", ClassName = "MSRS",
@@ -131,7 +143,7 @@ MSRS = {
--- MSRS class version. --- MSRS class version.
-- @field #string version -- @field #string version
MSRS.version="0.0.4" MSRS.version="0.0.6"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -150,8 +162,9 @@ MSRS.version="0.0.4"
-- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #string PathToSRS Path to the directory, where SRS is located.
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self -- @return #MSRS self
function MSRS:New(PathToSRS, Frequency, Modulation) function MSRS:New(PathToSRS, Frequency, Modulation, Volume)
-- Defaults. -- Defaults.
Frequency =Frequency or 143 Frequency =Frequency or 143
@@ -167,6 +180,12 @@ function MSRS:New(PathToSRS, Frequency, Modulation)
self:SetGender() self:SetGender()
self:SetCoalition() self:SetCoalition()
self:SetLabel() self:SetLabel()
self:SetVolume()
self.lid = string.format("%s-%s | ",self.name,self.version)
if not io or not os then
self:E(self.lid.."***** ERROR - io or os NOT desanitized! MSRS will not work!")
end
return self return self
end end
@@ -209,6 +228,24 @@ function MSRS:GetPath()
return self.path return self.path
end end
--- Set SRS volume.
-- @param #MSRS self
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self
function MSRS:SetVolume(Volume)
local volume = Volume or 1
if volume > 1 then volume = 1 elseif volume < 0 then volume = 0 end
self.volume = volume
return self
end
--- Get SRS volume.
-- @param #MSRS self
-- @return #number Volume Volume - 1.0 is max, 0.0 is silence
function MSRS:GetVolume()
return self.volume
end
--- Set label. --- Set label.
-- @param #MSRS self -- @param #MSRS self
-- @param #number Label. Default "ROBOT" -- @param #number Label. Default "ROBOT"
@@ -689,7 +726,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT") --local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
-- Command. -- Command.
local command=string.format('"%s\\%s" -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, label) local command=string.format('"%s\\%s" -f %s -m %s -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
-- Set voice or gender/culture. -- Set voice or gender/culture.
if voice then if voice then

View File

@@ -52,6 +52,7 @@ BIGSMOKEPRESET = {
-- @field #string TheChannel The Channel map. -- @field #string TheChannel The Channel map.
-- @field #string Syria Syria map. -- @field #string Syria Syria map.
-- @field #string MarianaIslands Mariana Islands map. -- @field #string MarianaIslands Mariana Islands map.
-- @field #string Falklands South Atlantic map.
DCSMAP = { DCSMAP = {
Caucasus="Caucasus", Caucasus="Caucasus",
NTTR="Nevada", NTTR="Nevada",
@@ -59,7 +60,8 @@ DCSMAP = {
PersianGulf="PersianGulf", PersianGulf="PersianGulf",
TheChannel="TheChannel", TheChannel="TheChannel",
Syria="Syria", Syria="Syria",
MarianaIslands="MarianaIslands" MarianaIslands="MarianaIslands",
Falklands="Falklands",
} }
@@ -1347,6 +1349,7 @@ end
-- * The Cannel Map -10 (West) -- * The Cannel Map -10 (West)
-- * Syria +5 (East) -- * Syria +5 (East)
-- * Mariana Islands +2 (East) -- * Mariana Islands +2 (East)
-- * Falklands +12 (East) - note there's a LOT of deviation across the map, as we're closer to the South Pole
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre -- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
-- @return #number Declination in degrees. -- @return #number Declination in degrees.
function UTILS.GetMagneticDeclination(map) function UTILS.GetMagneticDeclination(map)
@@ -1369,6 +1372,8 @@ function UTILS.GetMagneticDeclination(map)
declination=5 declination=5
elseif map==DCSMAP.MarianaIslands then elseif map==DCSMAP.MarianaIslands then
declination=2 declination=2
elseif map==DCSMAP.Falklands then
declination=12
else else
declination=0 declination=0
end end

View File

@@ -398,6 +398,7 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Ruwayshid -- * AIRBASE.Syria.Ruwayshid
-- * AIRBASE.Syria.Sanliurfa -- * AIRBASE.Syria.Sanliurfa
-- * AIRBASE.Syria.Tal_Siman -- * AIRBASE.Syria.Tal_Siman
-- * AIRBASE.Syria.Deir_ez_Zor
-- --
--@field Syria --@field Syria
AIRBASE.Syria={ AIRBASE.Syria={
@@ -463,7 +464,7 @@ AIRBASE.Syria={
["Ruwayshid"]="Ruwayshid", ["Ruwayshid"]="Ruwayshid",
["Sanliurfa"]="Sanliurfa", ["Sanliurfa"]="Sanliurfa",
["Tal_Siman"]="Tal Siman", ["Tal_Siman"]="Tal Siman",
["Deir_ez-Zor"] = "Deir ez-Zor", ["Deir_ez_Zor"] = "Deir ez-Zor",
} }
--- Airbases of the Mariana Islands map: --- Airbases of the Mariana Islands map:
@@ -485,6 +486,29 @@ AIRBASE.MarianaIslands = {
["Olf_Orote"] = "Olf Orote", ["Olf_Orote"] = "Olf Orote",
} }
--- Airbases of the South Atlantic map:
--
-- * AIRBASE.SouthAtlantic.Port_Stanley
-- * AIRBASE.SouthAtlantic.Mount_Pleasant
-- * AIRBASE.SouthAtlantic.San_Carlos_FOB
-- * AIRBASE.SouthAtlantic.Rio_Grande
-- * AIRBASE.SouthAtlantic.Rio_Gallegos
-- * AIRBASE.SouthAtlantic.Ushuaia
-- * AIRBASE.SouthAtlantic.Ushuaia_Helo_Port
-- * AIRBASE.SouthAtlantic.Punta_Arenas
--
--@field MarianaIslands
AIRBASE.SouthAtlantic={
["Port_Stanley"]="Port Stanley",
["Mount_Pleasant"]="Mount Pleasant",
["San_Carlos_FOB"]="San Carlos FOB",
["Rio_Grande"]="Rio Grande",
["Rio_Gallegos"]="Rio Gallegos",
["Ushuaia"]="Ushuaia",
["Ushuaia_Helo_Port"]="Ushuaia Helo Port",
["Punta_Arenas"]="Punta Arenas",
}
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". --- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
-- @type AIRBASE.ParkingSpot -- @type AIRBASE.ParkingSpot
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot. -- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.

View File

@@ -711,11 +711,24 @@ end
-- @return DCS#Unit The DCS Unit. -- @return DCS#Unit The DCS Unit.
function GROUP:GetDCSUnit( UnitNumber ) function GROUP:GetDCSUnit( UnitNumber )
local DCSGroup=self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
return DCSUnitFound if DCSGroup.getUnit and DCSGroup:getUnit( UnitNumber ) then
return DCSGroup:getUnit( UnitNumber )
else
local UnitFound = nil
-- 2.7.1 dead event bug, return the first alive unit instead
local units = DCSGroup:getUnits() or {}
for _,_unit in pairs(units) do
if _unit and _unit:isExist() then
return _unit
end
end
end
end end
return nil return nil
@@ -1093,13 +1106,15 @@ end
function GROUP:GetHeading() function GROUP:GetHeading()
self:F2(self.GroupName) self:F2(self.GroupName)
self:F2(self.GroupName)
local GroupSize = self:GetSize() local GroupSize = self:GetSize()
local HeadingAccumulator = 0 local HeadingAccumulator = 0
local n=0 local n=0
local Units = self:GetUnits()
if GroupSize then if GroupSize then
for i = 1, GroupSize do for _,unit in pairs(Units) do
local unit=self:GetUnit(i)
if unit and unit:IsAlive() then if unit and unit:IsAlive() then
HeadingAccumulator = HeadingAccumulator + unit:GetHeading() HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
n=n+1 n=n+1
@@ -1160,7 +1175,7 @@ function GROUP:GetFuelAvg()
local TotalFuel = 0 local TotalFuel = 0
for UnitID, UnitData in pairs( self:GetUnits() ) do for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
local UnitFuel = Unit:GetFuel() local UnitFuel = Unit:GetFuel() or 0
self:F( { Fuel = UnitFuel } ) self:F( { Fuel = UnitFuel } )
TotalFuel = TotalFuel + UnitFuel TotalFuel = TotalFuel + UnitFuel
end end

View File

@@ -1600,7 +1600,11 @@ do -- Cargo
["Ural-4320 APA-5D"] = 10, ["Ural-4320 APA-5D"] = 10,
["Ural-4320T"] = 14, ["Ural-4320T"] = 14,
["ZBD04A"] = 7, -- new by kappa ["ZBD04A"] = 7, -- new by kappa
["VAB_Mephisto"] = 8 -- new by Apple ["VAB_Mephisto"] = 8, -- new by Apple
["tt_KORD"] = 6, -- 2.7.1 HL/TT
["tt_DSHK"] = 6,
["HL_KORD"] = 6,
["HL_DSHK"] = 6,
} }
local CargoBayWeightLimit = (Weights[Desc.typeName] or 0) * 95 local CargoBayWeightLimit = (Weights[Desc.typeName] or 0) * 95

View File

@@ -119,7 +119,10 @@ function UNIT:Register( UnitName )
local unit=Unit.getByName(self.UnitName) local unit=Unit.getByName(self.UnitName)
if unit then if unit then
self.GroupName=unit:getGroup():getName() local group = unit:getGroup()
if group then
self.GroupName=group:getName()
end
end end
-- Set event prio. -- Set event prio.

View File

@@ -61,9 +61,10 @@ Documentation on the MOOSE class hierarchy, usage guides and background informat
## [MOOSE Youtube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) ## [MOOSE Youtube Tutorials](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
MOOSE has a [broadcast and training channel on YouTube](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) with various channels that you can watch. Pene has kindly created a [tutorial series for MOOSE](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
with various videos that you can watch.