Fixed link in Func.Range to "476 vFG - Air Weapons Range Objects"

# Conflicts:
#	Moose Development/Moose/Functional/Range.lua
This commit is contained in:
kaltokri 2024-05-21 19:48:01 +02:00
parent 42d8a9f59d
commit 59ceb01d19

View File

@ -7,7 +7,7 @@
-- Implementation is based on the [Simple Range Script](https://forums.eagle.ru/showthread.php?t=157991) by Ciribob, which itself was motivated
-- by a script by SNAFU [see here](https://forums.eagle.ru/showthread.php?t=109174).
--
-- [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is highly recommended for this class.
-- [476th - Air Weapons Range Objects mod](https://www.476vfightergroup.com/downloads.php?do=download&downloadid=482) is highly recommended for this class.
--
-- **Main Features:**
--
@ -46,7 +46,7 @@
--
-- ### Contributions: FlightControl, Ciribob
-- ### SRS Additions: Applevangelist
--
--
-- ===
-- @module Functional.Range
-- @image Range.JPG
@ -102,7 +102,7 @@
-- @field #string targetpath Path where to save the target sheets.
-- @field #string targetprefix File prefix for target sheet files.
-- @field Sound.SRS#MSRS controlmsrs SRS wrapper for range controller.
-- @field Sound.SRS#MSRSQUEUE controlsrsQ SRS queue for range controller.
-- @field Sound.SRS#MSRSQUEUE controlsrsQ SRS queue for range controller.
-- @field Sound.SRS#MSRS instructmsrs SRS wrapper for range instructor.
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
-- @field #number Coalition Coalition side for the menu, if any.
@ -169,7 +169,7 @@
--
-- ## Specifying Coordinates
--
-- It is also possible to specify coordinates rather than unit or static objects as bombing target locations. This has the advantage, that even when the unit/static object is dead, the specified
-- It is also possible to specify coordinates rather than unit or static objects as bombing target locations. This has the advantage, that even when the unit/static object is dead, the specified
-- coordinate will still be a valid impact point. This can be done via the @{#RANGE.AddBombingTargetCoordinate}(*coord*, *name*, *goodhitrange*) function.
--
-- # Fine Tuning
@ -231,8 +231,8 @@
-- By default, the sound files are placed in the "Range Soundfiles/" folder inside the mission (.miz) file. Another folder can be specified via the @{#RANGE.SetSoundfilesPath}(*path*) function.
--
-- ## Voice output via SRS
--
-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}().
--
-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}().
-- Range control and instructor frequencies and voices can then be set via @{#RANGE.SetSRSRangeControl}() and @{#RANGE.SetSRSRangeInstructor}().
--
-- # Persistence
@ -243,11 +243,11 @@
-- The next time you start the mission, these results are also automatically loaded.
--
-- 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}
@ -371,7 +371,7 @@ RANGE = {
-- @param #number boxlength Length of strafe pit box in meters.
-- @param #number boxwidth Width of strafe pit box in meters.
-- @param #number goodpass Number of hits for a good strafing pit pass.
-- @param #number foulline Distance of foul line in meters.
-- @param #number foulline Distance of foul line in meters.
RANGE.Defaults = {
goodhitrange = 25,
strafemaxalt = 914,
@ -625,9 +625,9 @@ function RANGE:New( RangeName, Coalition )
-- Get range name.
-- TODO: make sure that the range name is not given twice. This would lead to problems in the F10 radio menu.
self.rangename = RangeName or "Practice Range"
self.Coalition = Coalition
-- Log id.
self.lid = string.format( "RANGE %s | ", self.rangename )
@ -993,9 +993,9 @@ end
-- @param #string Host Host. Default "127.0.0.1".
-- @return #RANGE self
function RANGE:SetFunkManOn(Port, Host)
self.funkmanSocket=SOCKET:New(Port, Host)
return self
end
@ -1200,7 +1200,7 @@ end
-- @param #string PathToSRS Path to SRS directory.
-- @param #number Port SRS port. Default 5002.
-- @param #number Coalition Coalition side, e.g. `coalition.side.BLUE` or `coalition.side.RED`. Default `coalition.side.BLUE`.
-- @param #number Frequency Frequency to use. Default is 256 MHz for range control and 305 MHz for instructor. If given, both control and instructor get this frequency.
-- @param #number Frequency Frequency to use. Default is 256 MHz for range control and 305 MHz for instructor. If given, both control and instructor get this frequency.
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM
-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0
-- @param #string PathToGoogleKey Path to Google TTS credentials.
@ -1208,9 +1208,9 @@ end
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
if PathToSRS or MSRS.path then
self.useSRS=true
self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM)
self.controlmsrs:SetPort(Port or MSRS.port)
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
@ -1224,14 +1224,14 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
self.instructmsrs:SetLabel("RANGEI")
self.instructmsrs:SetVolume(Volume or 1.0)
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
if PathToGoogleKey then
if PathToGoogleKey then
self.controlmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
self.controlmsrs:SetProvider(MSRS.Provider.GOOGLE)
self.instructmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
self.instructmsrs:SetProvider(MSRS.Provider.GOOGLE)
end
else
self:E(self.lid..string.format("ERROR: No SRS path specified!"))
end
@ -1741,9 +1741,9 @@ end
-- @param Core.Event#EVENTDATA EventData
function RANGE:OnEventBirth( EventData )
self:F( { eventbirth = EventData } )
if not EventData.IniPlayerName then return end
local _unitName = EventData.IniUnitName
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
@ -1764,7 +1764,7 @@ function RANGE:OnEventBirth( EventData )
-- Reset current strafe status.
self.strafeStatus[_uid] = nil
if self.Coalition then
if EventData.IniCoalition == self.Coalition then
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
@ -1773,7 +1773,7 @@ function RANGE:OnEventBirth( EventData )
-- Add Menu commands after a delay of 0.1 seconds.
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
end
-- By default, some bomb impact points and do not flare each hit on target.
self.PlayerSettings[_playername] = {} -- #RANGE.PlayerData
self.PlayerSettings[_playername].smokebombimpact = self.defaultsmokebomb
@ -1907,21 +1907,21 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
local _distance = nil
local _closeCoord = nil --Core.Point#COORDINATE
local _hitquality = "POOR"
-- Get callsign.
local _callsign = self:_myname( playerData.unitname )
local _playername=playerData.playername
local _unit=playerData.unit
-- Coordinate of impact point.
local impactcoord = weapon:GetImpactCoordinate()
-- Check if impact happened in range zone.
local insidezone = self.rangezone:IsCoordinateInZone( impactcoord )
-- Smoke impact point of bomb.
if playerData.smokebombimpact and insidezone then
if playerData.delaysmoke then
@ -1930,19 +1930,19 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
impactcoord:Smoke( playerData.smokecolor )
end
end
-- Loop over defined bombing targets.
for _, _bombtarget in pairs( self.bombingTargets ) do
local bombtarget=_bombtarget --#RANGE.BombTarget
-- Get target coordinate.
local targetcoord = self:_GetBombTargetCoordinate( _bombtarget )
if targetcoord then
-- Distance between bomb and target.
local _temp = impactcoord:Get2DDistance( targetcoord )
-- Find closest target to last known position of the bomb.
if _distance == nil or _temp < _distance then
_distance = _temp
@ -1959,21 +1959,21 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
else
_hitquality = "POOR"
end
end
end
end
-- Count if bomb fell less than ~1 km away from the target.
if _distance and _distance <= self.scorebombdistance then
-- Init bomb player results.
if not self.bombPlayerResults[_playername] then
self.bombPlayerResults[_playername] = {}
end
-- Local results.
local _results = self.bombPlayerResults[_playername]
local result = {} -- #RANGE.BombResult
result.command=SOCKET.DataType.BOMBRESULT
result.name = _closetTarget.name or "unknown"
@ -1995,24 +1995,24 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
result.attackVel = attackVel
result.attackAlt = attackAlt
result.date=os and os.date() or "n/a"
-- Add to table.
table.insert( _results, result )
-- Call impact.
self:Impact( result, playerData )
elseif insidezone then
-- Send message.
-- DONE SRS message
local _message = string.format( "%s, weapon impacted too far from nearest range target (>%.1f km). No score!", _callsign, self.scorebombdistance / 1000 )
if self.useSRS then
local ttstext = string.format( "%s, weapon impacted too far from nearest range target, mor than %.1f kilometer. No score!", _callsign, self.scorebombdistance / 1000 )
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
end
self:_DisplayMessageToGroup( _unit, _message, nil, false )
if self.rangecontrol then
-- weapon impacted too far from the nearest target! No Score!
if self.useSRS then
@ -2021,11 +2021,11 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
self.rangecontrol:NewTransmission( RANGE.Sound.RCWeaponImpactedTooFar.filename, RANGE.Sound.RCWeaponImpactedTooFar.duration, self.soundpath, nil, nil, _message, self.subduration )
end
end
else
self:T( self.lid .. "Weapon impacted outside range zone." )
end
end
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
@ -2038,7 +2038,7 @@ function RANGE:OnEventShot( EventData )
if EventData.Weapon == nil or EventData.IniDCSUnit == nil or EventData.IniPlayerName == nil then
return
end
-- Create weapon object.
local weapon=WEAPON:New(EventData.weapon)
@ -2050,7 +2050,7 @@ function RANGE:OnEventShot( EventData )
-- Get player unit and name.
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
-- Distance Player-to-Range. Set this to larger value than the threshold.
local dPR = self.BombtrackThreshold * 2
@ -2065,16 +2065,16 @@ function RANGE:OnEventShot( EventData )
-- Player data.
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
-- Attack parameters.
local attackHdg=_unit:GetHeading()
local attackAlt=_unit:GetHeight()
attackAlt = UTILS.MetersToFeet(attackAlt)
local attackVel=_unit:GetVelocityKNOTS()
local attackVel=_unit:GetVelocityKNOTS()
-- Tracking info and init of last bomb position.
self:T( self.lid .. string.format( "RANGE %s: Tracking %s - %s.", self.rangename, weapon:GetTypeName(), weapon:GetName()))
-- Set callback function on impact.
weapon:SetFuncImpact(RANGE._OnImpact, self, playerData, attackHdg, attackAlt, attackVel)
@ -2146,33 +2146,33 @@ end
function RANGE:onafterEnterRange( From, Event, To, player )
if self.instructor and self.rangecontrol then
if self.useSRS then
local text = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz", self.rangecontrolfreq)
local ttstext = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.", self.rangecontrolfreq)
local group = player.client:GetGroup()
self.instructsrsQ:NewTransmission(ttstext, nil, self.instructmsrs, nil, 1, {group}, text, 10)
else
-- Range control radio frequency split.
local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." )
-- Radio message that player entered the range
-- You entered the bombing range. For hit assessment, contact the range controller at xy MHz
self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath )
self.instructor:Number2Transmission( RF[1] )
if tonumber( RF[2] ) > 0 then
self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath )
self.instructor:Number2Transmission( RF[2] )
end
self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath )
end
end
@ -2190,11 +2190,11 @@ function RANGE:onafterExitRange( From, Event, To, player )
if self.instructor then
-- You left the bombing range zone. Have a nice day!
if self.useSRS then
local text = "You left the bombing range zone. "
local r=math.random(5)
if r==1 then
text=text.."Have a nice day!"
elseif r==2 then
@ -2204,9 +2204,9 @@ function RANGE:onafterExitRange( From, Event, To, player )
elseif r==4 then
text=text.."See you in two weeks!"
elseif r==5 then
text=text.."!"
text=text.."!"
end
self.instructsrsQ:NewTransmission(text, nil, self.instructmsrs, nil, 1, {player.client:GetGroup()}, text, 10)
else
self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath )
@ -2240,7 +2240,7 @@ function RANGE:onafterImpact( From, Event, To, result, player )
text = text .. string.format( " %s hit.", result.quality )
if self.rangecontrol then
if self.useSRS then
local group = player.client:GetGroup()
self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10)
@ -2265,10 +2265,10 @@ function RANGE:onafterImpact( From, Event, To, result, player )
-- Unit.
if player.unitname and not self.useSRS then
-- Get unit.
local unit = UNIT:FindByName( player.unitname )
-- Send message.
self:_DisplayMessageToGroup( unit, text, nil, true )
self:T( self.lid .. text )
@ -2278,7 +2278,7 @@ function RANGE:onafterImpact( From, Event, To, result, player )
if self.autosave then
self:Save()
end
-- Send result to FunkMan, which creates fancy MatLab figures and sends them to Discord via a bot.
if self.funkmanSocket then
self.funkmanSocket:SendTable(result)
@ -2547,7 +2547,7 @@ function RANGE:_DisplayMyStrafePitResults( _unitName )
local _message = string.format( "My Top %d Strafe Pit Results:\n", self.ndisplayresult )
-- Get player results.
local _results = self.strafePlayerResults[_playername]
local _results = self.strafePlayerResults[_playername]
-- Create message.
if _results == nil then
@ -2853,7 +2853,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
end
end
text = text .. string.format( "Instructor %.3f MHz (Relay=%s)\n", self.instructorfreq, alive )
end
end
if self.rangecontrol then
local alive = "N/A"
if self.rangecontrolrelayname then
@ -3081,10 +3081,10 @@ function RANGE:_CheckInZone( _unitName )
local unitheading = 0 -- RangeBoss
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.
local function checkme( targetheading, _zone )
local zone = _zone -- Core.Zone#ZONE
@ -3098,7 +3098,7 @@ function RANGE:_CheckInZone( _unitName )
if towardspit then
local vec3 = _unit:GetVec3()
local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2
local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2
local landheight = land.getHeight( vec2 )
local unitalt = vec3.y - landheight
@ -3145,7 +3145,7 @@ function RANGE:_CheckInZone( _unitName )
-- Send message.
self:_DisplayMessageToGroup( _unit, _msg, nil, true )
if self.rangecontrol then
if self.useSRS then
local group = _unit:GetGroup()
@ -3164,9 +3164,9 @@ function RANGE:_CheckInZone( _unitName )
-- Result.
local _result = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
local _sound = nil -- #RANGE.Soundfile
-- Calculate accuracy of run. Number of hits wrt number of rounds fired.
local shots = _result.ammo - _ammo
local accur = 0
@ -3176,7 +3176,7 @@ function RANGE:_CheckInZone( _unitName )
accur = 100
end
end
-- Results text and sound message.
local resulttext=""
if _result.pastfoulline == true then --
@ -3213,7 +3213,7 @@ function RANGE:_CheckInZone( _unitName )
-- Send message.
self:_DisplayMessageToGroup( _unit, _text )
-- Strafe result.
local result = {} -- #RANGE.StrafeResult
result.command=SOCKET.DataType.STRAFERESULT
@ -3230,14 +3230,14 @@ function RANGE:_CheckInZone( _unitName )
result.rangename = self.rangename
result.airframe=playerData.airframe
result.invalid = _result.pastfoulline
-- Griger Results.
self:StrafeResult(playerData, result)
-- Save trap sheet.
if playerData and playerData.targeton and self.targetsheet then
self:_SaveTargetSheet( _playername, result )
end
end
-- Voice over.
if self.rangecontrol then
@ -3302,7 +3302,7 @@ function RANGE:_CheckInZone( _unitName )
-- Send message.
self:_DisplayMessageToGroup( _unit, _msg, 10, true )
-- Trigger event that player is rolling in.
self:RollingIn(playerData, target)
@ -3438,18 +3438,18 @@ function RANGE:_GetBombTargetCoordinate( target )
local coord = nil -- Core.Point#COORDINATE
if target.type == RANGE.TargetType.UNIT then
-- Check if alive
if target.target and target.target:IsAlive() then
-- Get current position.
coord = target.target:GetCoordinate()
-- Save as last known position in case target dies.
target.coordinate=coord
target.coordinate=coord
else
-- Use stored position.
coord = target.coordinate
end
elseif target.type == RANGE.TargetType.STATIC then
-- Static targets dont move.
@ -3459,11 +3459,11 @@ function RANGE:_GetBombTargetCoordinate( target )
-- Coordinates dont move.
coord = target.coordinate
elseif target.type == RANGE.TargetType.SCENERY then
-- Coordinates dont move.
coord = target.coordinate
coord = target.coordinate
else
self:E( self.lid .. "ERROR: Unknown target type." )
@ -3670,7 +3670,7 @@ function RANGE:_DisplayMessageToGroup( _unit, _text, _time, _clear, display, _to
local playermessage = self.PlayerSettings[playername].messages
-- Send message to player if messages enabled and not only for the examiner.
if _gid and (playermessage == true or display) and (not self.examinerexclusive) then
if _togroup and _grp then
local m = MESSAGE:New(_text,_time,nil,_clear):ToGroup(_grp)
@ -4025,9 +4025,9 @@ function RANGE:_GetPlayerUnitAndName( _unitName )
self:F2( _unitName )
if _unitName ~= nil then
local multiplayer = false
-- Get DCS unit from its name.
local DCSunit = Unit.getByName( _unitName )
@ -4066,7 +4066,7 @@ function RANGE:_myname( unitname )
if grp and grp:IsAlive() then
pname = grp:GetCustomCallSign(true,true)
end
end
end
return pname
end