Merge branch 'FF/Ops' into FF/OpsDev

This commit is contained in:
Frank 2023-02-16 17:30:47 +01:00
commit 91e80bf1a6
7 changed files with 454 additions and 66 deletions

View File

@ -1054,28 +1054,55 @@ do -- COORDINATE
return heading return heading
end end
--- Returns the 3D wind direction vector. Note that vector points into the direction the wind in blowing to.
-- @param #COORDINATE self
-- @param #number height (Optional) parameter specifying the height ASL in meters. The minimum height will be always be the land height since the wind is zero below the ground.
-- @param #boolean turbulence (Optional) If `true`, include turbulence.
-- @return DCS#Vec3 Wind 3D vector. Components in m/s.
function COORDINATE:GetWindVec3(height, turbulence)
-- We at 0.1 meters to be sure to be above ground since wind is zero below ground level.
local landheight=self:GetLandHeight()+0.1
local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z}
-- Get wind velocity vector.
local wind = nil --DCS#Vec3
if turbulence then
wind = atmosphere.getWindWithTurbulence(point)
else
wind = atmosphere.getWind(point)
end
return wind
end
--- Returns the wind direction (from) and strength. --- Returns the wind direction (from) and strength.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground. -- @param #number height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground.
-- @return Direction the wind is blowing from in degrees. -- @param #boolean turbulence If `true`, include turbulence. If `false` or `nil`, wind without turbulence.
-- @return Wind strength in m/s. -- @return #number Direction the wind is blowing from in degrees.
function COORDINATE:GetWind(height) -- @return #number Wind strength in m/s.
local landheight=self:GetLandHeight()+0.1 -- we at 0.1 meters to be sure to be above ground since wind is zero below ground level. function COORDINATE:GetWind(height, turbulence)
local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z}
-- get wind velocity vector -- Get wind velocity vector
local wind = atmosphere.getWind(point) local wind = self:GetWindVec3(height, turbulence)
local direction = math.deg(math.atan2(wind.z, wind.x))
if direction < 0 then -- Calculate the direction of the vector.
direction = 360 + direction local direction=UTILS.VecHdg(wind)
end
-- Convert to direction to from direction -- Invert "to" direction to "from" direction.
if direction > 180 then if direction > 180 then
direction = direction-180 direction = direction-180
else else
direction = direction+180 direction = direction+180
end end
local strength=math.sqrt((wind.x)^2+(wind.z)^2)
-- Return wind direction and strength km/h. -- Wind strength in m/s.
local strength=UTILS.VecNorm(wind) -- math.sqrt((wind.x)^2+(wind.z)^2)
-- Return wind direction and strength.
return direction, strength return direction, strength
end end

View File

@ -5,7 +5,7 @@
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: -- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
-- --
-- * Spot for a defined duration. -- * Spot for a defined duration.
-- * Updates of laer spot position every 0.2 seconds for moving targets. -- * Updates of laser spot position every 0.2 seconds for moving targets.
-- * Wiggle the spot at the target. -- * Wiggle the spot at the target.
-- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Provide a @{Wrapper.Unit} as a target, instead of a point.
-- * Implement a status machine, LaseOn, LaseOff. -- * Implement a status machine, LaseOn, LaseOff.
@ -14,17 +14,7 @@
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [SPOT Demo Missions source code]() -- ### [Demo Missions on GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
--
-- ### [SPOT Demo Missions, only for beta testers]()
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ===
--
-- # YouTube Channel
--
-- ### [SPOT YouTube Channel]()
-- --
-- === -- ===
-- --
@ -50,14 +40,14 @@ do
--- Implements the target spotting or marking functionality, but adds additional luxury to be able to: --- Implements the target spotting or marking functionality, but adds additional luxury to be able to:
-- --
-- * Mark targets for a defined duration. -- * Mark targets for a defined duration.
-- * Updates of laer spot position every 0.2 seconds for moving targets. -- * Updates of laser spot position every 0.25 seconds for moving targets.
-- * Wiggle the spot at the target. -- * Wiggle the spot at the target.
-- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Provide a @{Wrapper.Unit} as a target, instead of a point.
-- * Implement a status machine, LaseOn, LaseOff. -- * Implement a status machine, LaseOn, LaseOff.
-- --
-- ## 1. SPOT constructor -- ## 1. SPOT constructor
-- --
-- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object. -- * @{#SPOT.New}(): Creates a new SPOT object.
-- --
-- ## 2. SPOT is a FSM -- ## 2. SPOT is a FSM
-- --
@ -218,6 +208,8 @@ do
self.Recce = Recce self.Recce = Recce
self.RecceName = self.Recce:GetName()
self.LaseScheduler = SCHEDULER:New( self ) self.LaseScheduler = SCHEDULER:New( self )
self:SetEventPriority( 5 ) self:SetEventPriority( 5 )
@ -243,6 +235,9 @@ do
end end
self.Target = Target self.Target = Target
self.TargetName = Target:GetName()
self.LaserCode = LaserCode self.LaserCode = LaserCode
self.Lasing = true self.Lasing = true
@ -302,12 +297,18 @@ do
function SPOT:OnEventDead(EventData) function SPOT:OnEventDead(EventData)
self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
if self.Target then if self.Target then
if EventData.IniDCSUnitName == self.Target:GetName() then if EventData.IniDCSUnitName == self.TargetName then
self:F( {"Target dead ", self.Target:GetName() } ) self:F( {"Target dead ", self.TargetName } )
self:Destroyed() self:Destroyed()
self:LaseOff() self:LaseOff()
end end
end end
if self.Recce then
if EventData.IniDCSUnitName == self.RecceName then
self:F( {"Recce dead ", self.RecceName } )
self:LaseOff()
end
end
end end
--- @param #SPOT self --- @param #SPOT self

View File

@ -1369,6 +1369,13 @@ PLAYERTASKCONTROLLER.Messages = {
AIRDEFENSE = "Airdefense", AIRDEFENSE = "Airdefense",
SAM = "SAM", SAM = "SAM",
GROUP = "Group", GROUP = "Group",
UNARMEDSHIP = "Merchant",
LIGHTARMEDSHIP = "Light Boat",
CORVETTE = "Corvette",
FRIGATE = "Frigate",
CRUISER = "Cruiser",
DESTROYER = "Destroyer",
CARRIER = "Aircraft Carrier",
}, },
DE = { DE = {
TASKABORT = "Auftrag abgebrochen!", TASKABORT = "Auftrag abgebrochen!",
@ -1441,12 +1448,19 @@ PLAYERTASKCONTROLLER.Messages = {
AIRDEFENSE = "Flak", AIRDEFENSE = "Flak",
SAM = "Luftabwehr", SAM = "Luftabwehr",
GROUP = "Einheit", GROUP = "Einheit",
UNARMEDSHIP = "Handelsschiff",
LIGHTARMEDSHIP = "Tender",
CORVETTE = "Korvette",
FRIGATE = "Fregatte",
CRUISER = "Kreuzer",
DESTROYER = "Zerstörer",
CARRIER = "Flugzeugträger",
}, },
} }
--- PLAYERTASK class version. --- PLAYERTASK class version.
-- @field #string version -- @field #string version
PLAYERTASKCONTROLLER.version="0.1.56" PLAYERTASKCONTROLLER.version="0.1.58"
--- Create and run a new TASKCONTROLLER instance. --- Create and run a new TASKCONTROLLER instance.
-- @param #PLAYERTASKCONTROLLER self -- @param #PLAYERTASKCONTROLLER self
@ -2231,6 +2245,8 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue()
end end
end end
if object:IsInstanceOf("UNIT") or object:IsInstanceOf("GROUP") then
if self.UseTypeNames and object:IsGround() then if self.UseTypeNames and object:IsGround() then
-- * Threat level 0: Unit is unarmed. -- * Threat level 0: Unit is unarmed.
-- * Threat level 1: Unit is infantry. -- * Threat level 1: Unit is infantry.
@ -2262,6 +2278,29 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue()
--self:T(self.lid.."Target TypeName = "..target.TypeName) --self:T(self.lid.."Target TypeName = "..target.TypeName)
end end
if self.UseTypeNames and object:IsShip() then
local threat = object:GetThreatLevel()
local typekey = "UNARMEDSHIP"
if threat == 1 then
typekey = "LIGHTARMEDSHIP"
elseif threat == 2 then
typekey = "CORVETTE"
elseif threat == 3 or threat == 4 then
typekey = "FRIGATE"
elseif threat == 5 or threat == 6 then
typekey = "CRUISER"
elseif threat == 7 or threat == 8 then
typekey = "DESTROYER"
elseif threat >= 9 then
typekey = "CARRIER"
end
local typename = self.gettext:GetEntry(typekey,self.locale)
target.TypeName = typename
--self:T(self.lid.."Target TypeName = "..target.TypeName)
end
end
self:_AddTask(target) self:_AddTask(target)
end end
return self return self

View File

@ -489,6 +489,31 @@ UTILS.hPa2inHg = function( hPa )
return hPa * 0.0295299830714 return hPa * 0.0295299830714
end end
--- Convert indicated airspeed (IAS) to true airspeed (TAS) for a given altitude above main sea level.
-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level.
-- @param #number ias Indicated air speed in any unit (m/s, km/h, knots, ...)
-- @param #number altitude Altitude above main sea level in meters.
-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017.
-- @return #number True airspeed in the same unit the IAS has been given.
UTILS.IasToTas = function( ias, altitude, oatcorr )
oatcorr=oatcorr or 0.017
local tas=ias + (ias * oatcorr * UTILS.MetersToFeet(altitude) / 1000)
return tas
end
--- Convert true airspeed (TAS) to indicated airspeed (IAS) for a given altitude above main sea level.
-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level.
-- @param #number tas True air speed in any unit (m/s, km/h, knots, ...)
-- @param #number altitude Altitude above main sea level in meters.
-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017.
-- @return #number Indicated airspeed in the same unit the TAS has been given.
UTILS.TasToIas = function( tas, altitude, oatcorr )
oatcorr=oatcorr or 0.017
local ias=tas/(1+oatcorr*UTILS.MetersToFeet(altitude)/1000)
return ias
end
--- Convert knots to altitude corrected KIAS, e.g. for tankers. --- Convert knots to altitude corrected KIAS, e.g. for tankers.
-- @param #number knots Speed in knots. -- @param #number knots Speed in knots.
-- @param #number altitude Altitude in feet -- @param #number altitude Altitude in feet

View File

@ -1,4 +1,4 @@
--- **Wrapper** - GROUP wraps the DCS Class Group objects. ----- **Wrapper** - GROUP wraps the DCS Class Group objects.
-- --
-- === -- ===
-- --
@ -2810,7 +2810,7 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
if self:IsAlive() then if self:IsAlive() then
local IsPlayer = self:IsPlayer() local IsPlayer = self:IsPlayer()
local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1 local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1
local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi local callsignroot = string.match(shortcallsign, '(%a+)') or "Ghost" -- Uzi
--self:I("CallSign = " .. callsignroot) --self:I("CallSign = " .. callsignroot)
local groupname = self:GetName() local groupname = self:GetName()
local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91 local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91

View File

@ -16,14 +16,27 @@ do
-- @type NET -- @type NET
-- @field #string ClassName -- @field #string ClassName
-- @field #string Version -- @field #string Version
-- @extends Core.Base#BASE -- @field #string lid
-- @field #number BlockTime
-- @field #table BlockedPilots
-- @field #table KnownPilots
-- @field #string BlockMessage
-- @field #string UnblockMessage
-- @extends Core.Fsm#FSM
--- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) --- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net)
-- with some added FSM functions and options to block/unblock players in MP environments.
-- --
-- @field #NET -- @field #NET
NET = { NET = {
ClassName = "NET", ClassName = "NET",
Version = "0.0.2" Version = "0.0.3",
BlockTime = 600,
BlockedPilots = {},
KnownPilots = {},
BlockMessage = nil,
UnblockMessage = nil,
lid = nil,
} }
--- Instantiate a new NET object. --- Instantiate a new NET object.
@ -31,7 +44,218 @@ NET = {
-- @return #NET self -- @return #NET self
function NET:New() function NET:New()
-- Inherit base. -- Inherit base.
local self = BASE:Inherit(self, BASE:New()) -- #NET local self = BASE:Inherit(self, FSM:New()) -- #NET
self.BlockTime = 600
self.BlockedPilots = {}
self.KnownPilots = {}
self:SetBlockMessage()
self:SetUnblockMessage()
self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler)
self:HandleEvent(EVENTS.PilotDead,self._EventHandler)
self:HandleEvent(EVENTS.Ejection,self._EventHandler)
self:HandleEvent(EVENTS.Crash,self._EventHandler)
self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler)
-- Start State.
self:SetStartState("Running")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "Run", "Running") -- Start FSM.
self:AddTransition("*", "PlayerJoined", "*")
self:AddTransition("*", "PlayerLeft", "*")
self:AddTransition("*", "PlayerDied", "*")
self:AddTransition("*", "PlayerEjected", "*")
self:AddTransition("*", "PlayerBlocked", "*")
self:AddTransition("*", "PlayerUnblocked", "*")
self.lid = string.format("NET %s | ",self.Version)
--- FSM Function OnAfterPlayerJoined.
-- @function [parent=#NET] OnAfterPlayerJoined
-- @param #NET self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Unit#UNIT Client Unit Object.
-- @param #string Name Name of joining Pilot.
-- @return #NET self
--- FSM Function OnAfterPlayerLeft.
-- @function [parent=#NET] OnAfterPlayerLeft
-- @param #NET self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Unit#UNIT Client Unit Object.
-- @param #string Name Name of leaving Pilot.
-- @return #NET self
--- FSM Function OnAfterPlayerEjected.
-- @function [parent=#NET] OnAfterPlayerEjected
-- @param #NET self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Unit#UNIT Client Unit Object.
-- @param #string Name Name of leaving Pilot.
-- @return #NET self
--- FSM Function OnAfterPlayerDied.
-- @function [parent=#NET] OnAfterPlayerDied
-- @param #NET self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Unit#UNIT Client Unit Object.
-- @param #string Name Name of dead Pilot.
-- @return #NET self
--- FSM Function OnAfterPlayerBlocked.
-- @function [parent=#NET] OnAfterPlayerBlocked
-- @param #NET self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Client#CLIENT Client Client Object.
-- @param #string Name Name of blocked Pilot.
-- @param #number Seconds Blocked for this number of seconds
-- @return #NET self
--- FSM Function OnAfterPlayerUnblocked.
-- @function [parent=#NET] OnAfterPlayerUnblocked
-- @param #NET self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Client#CLIENT Client Client Object.
-- @param #string Name Name of unblocked Pilot.
-- @return #NET self
return self
end
--- [Internal] Event Handler
-- @param #NET self
-- @param Core.Event#EVENTDATA EventData
-- @return #NET self
function NET:_EventHandler(EventData)
self:T(self.lid .. " _EventHandler")
self:T2({Event = EventData.id})
local data = EventData -- Core.Event#EVENTDATA EventData
if data.id and data.IniUnit and (data.IniPlayerName or data.IniUnit:GetPlayerName()) then
-- Get PlayerName
local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName()
self:T(self.lid.."Event for: "..name)
-- Joining
if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then
-- Check for known pilots
local TNow = timer.getTime()
if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then
-- block pilot
self:ReturnToSpectators(data.IniUnit)
else
self.KnownPilots[name] = true
self.BlockedPilots[name] = nil
self:__PlayerJoined(1,data.IniUnit,name)
return self
end
end
-- Leaving
if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then
self:__PlayerLeft(1,data.IniUnit,name)
self.KnownPilots[name] = false
return self
end
-- Ejected
if data.id == EVENTS.Ejection and self.KnownPilots[name] then
self:__PlayerEjected(1,data.IniUnit,name)
self.KnownPilots[name] = false
return self
end
-- Dead, Crash, Suicide
if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then
self:__PlayerDied(1,data.IniUnit,name)
self.KnownPilots[name] = false
return self
end
end
return self
end
--- Block a player.
-- @param #NET self
-- @param Wrapper.Client#CLIENT Client CLIENT object.
-- @param #string PlayerName (optional) Name of the player.
-- @param #number Seconds (optional) Number of seconds the player has to wait before rejoining.
-- @param #string Message (optional) Message to be sent via chat.
-- @return #NET self
function NET:BlockPlayer(Client,PlayerName,Seconds,Message)
local name
if Client then
name = CLIENT:GetPlayerName()
elseif PlayerName then
name = PlayerName
else
self:F(self.lid.."Block: No PlayerName given or not found!")
return self
end
local addon = Seconds or self.BlockTime
self.BlockedPilots[name] = timer.getTime()+addon
local message = Message or self.BlockMessage
if Client then
self:SendChatToPlayer(message,Client)
else
self:SendChat(name..": "..message)
end
self:__PlayerBlocked(1,Client,name,Seconds)
self:ReturnToSpectators(Client)
return self
end
--- Unblock a player.
-- @param #NET self
-- @param Wrapper.Client#CLIENT Client CLIENT object
-- @param #string PlayerName (optional) Name of the player.
-- @param #string Message (optional) Message to be sent via chat.
-- @return #NET self
function NET:UnblockPlayer(Client,PlayerName,Message)
local name
if Client then
name = CLIENT:GetPlayerName()
elseif PlayerName then
name = PlayerName
else
self:F(self.lid.."Unblock: No PlayerName given or not found!")
return self
end
self.BlockedPilots[name] = nil
local message = Message or self.UnblockMessage
if Client then
self:SendChatToPlayer(message,Client)
else
self:SendChat(name..": "..message)
end
self:__PlayerUnblocked(1,Client,name)
return self
end
function NET:SetBlockMessage(Text)
self.BlockMessage = Text or "You are blocked from joining. Wait time is: "..self.BlockTime.." seconds!"
return self
end
function NET:SetBlockTime(Seconds)
self.BlockTime = Seconds or 600
return self
end
function NET:SetUnblockMessage(Text)
self.UnblockMessage = Text or "You are unblocked now and can join again."
return self return self
end end

View File

@ -845,6 +845,78 @@ function POSITIONABLE:GetVelocityKNOTS()
return UTILS.MpsToKnots( self:GetVelocityMPS() ) return UTILS.MpsToKnots( self:GetVelocityMPS() )
end end
--- Returns the true airspeed (TAS). This is calculated from the current velocity minus wind in 3D.
-- @param #POSITIONABLE self
-- @return #number TAS in m/s. Returns 0 if the POSITIONABLE does not exist.
function POSITIONABLE:GetAirspeedTrue()
-- TAS
local tas=0
-- Get current coordinate.
local coord=self:GetCoord()
if coord then
-- Altitude in meters.
local alt=coord.y
-- Wind velocity vector.
local wvec3=coord:GetWindVec3(alt, false)
-- Velocity vector.
local vvec3=self:GetVelocityVec3()
--GS=TAS+WIND ==> TAS=GS-WIND
local tasvec3=UTILS.VecSubstract(vvec3, wvec3)
-- True airspeed in m/s
tas=UTILS.VecNorm(tasvec3)
end
return tas
end
--- Returns the indicated airspeed (IAS).
-- The IAS is calculated from the TAS under the approximation that TAS increases by ~2% with every 1000 feet altitude ASL.
-- @param #POSITIONABLE self
-- @param #number oatcorr (Optional) Outside air temperature (OAT) correction factor. Default 0.017 (=1.7%).
-- @return #number IAS in m/s. Returns 0 if the POSITIONABLE does not exist.
function POSITIONABLE:GetAirspeedIndicated(oatcorr)
-- Get true airspeed.
local tas=self:GetAirspeedTrue()
-- Get altitude.
local altitude=self:GetAltitude()
-- Convert TAS to IAS.
local ias=UTILS.TasToIas(tas, altitude, oatcorr)
return ias
end
--- Returns the horizonal speed relative to eath's surface. The vertical component of the velocity vector is projected out (set to zero).
-- @param #POSITIONABLE self
-- @return #number Ground speed in m/s. Returns 0 if the POSITIONABLE does not exist.
function POSITIONABLE:GetGroundSpeed()
local gs=0
local vel=self:GetVelocityVec3()
if vel then
local vec2={x=vel.x, y=vel.z}
gs=UTILS.Vec2Norm(vel)
end
return gs
end
--- Returns the Angle of Attack of a POSITIONABLE. --- Returns the Angle of Attack of a POSITIONABLE.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #number Angle of attack in degrees. -- @return #number Angle of attack in degrees.