mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
CT v0.1.7
This commit is contained in:
parent
087ac992a2
commit
1febe765bf
@ -460,12 +460,12 @@ do -- COORDINATE
|
||||
--- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance Distance The Distance to be added in meters.
|
||||
-- @param DCS#Angle Angle The Angle in degrees.
|
||||
-- @param DCS#Angle Angle The Angle in degrees. Defaults to 0 if not specified (nil).
|
||||
-- @return #COORDINATE The new calculated COORDINATE.
|
||||
function COORDINATE:Translate( Distance, Angle )
|
||||
local SX = self.x
|
||||
local SY = self.z
|
||||
local Radians = Angle / 180 * math.pi
|
||||
local Radians = (Angle or 0) / 180 * math.pi
|
||||
local TX = Distance * math.cos( Radians ) + SX
|
||||
local TY = Distance * math.sin( Radians ) + SY
|
||||
|
||||
|
||||
@ -195,6 +195,49 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new @{Static} from a COORDINATE.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static.
|
||||
-- @param #number Heading (Optional) Heading The heading of the static, which is a number in degrees from 0 to 360. Default is 0 degrees.
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName) --R2.4
|
||||
self:F( { PointVec2, Heading, NewName } )
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
Heading=Heading or 0
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
|
||||
StaticUnitTemplate.x = Coordinate.x
|
||||
StaticUnitTemplate.y = Coordinate.z
|
||||
StaticUnitTemplate.alt = Coordinate.y
|
||||
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticUnitTemplate.name = StaticTemplate.name
|
||||
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
|
||||
|
||||
self:F({StaticTemplate = StaticTemplate})
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Respawns the original @{Static}.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @return #SPAWNSTATIC
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **Bankler** (original idea and script), **funkyfranky** (MOOSE class implementation and enhancements)
|
||||
-- ### Authors: **funkyfranky** (MOOSE class implementation and enhancements), **Bankler** (original idea and script)
|
||||
--
|
||||
-- @module Functional.CarrierTrainer
|
||||
-- @image MOOSE.JPG
|
||||
@ -151,7 +151,7 @@ CARRIERTRAINER.LSOcall={
|
||||
BOLTER=USERSOUND:New("LSO - Bolter.ogg"),
|
||||
BOLTERT="Bolter, Bolter!",
|
||||
LONGGROOVE=USERSOUND:New("LSO - Long in Groove.ogg"),
|
||||
LONGGROOVET="You're lon in the groove. Depart and re-enter.",
|
||||
LONGGROOVET="You're long in the groove. Depart and re-enter.",
|
||||
}
|
||||
|
||||
--- Difficulty level.
|
||||
@ -160,24 +160,28 @@ CARRIERTRAINER.LSOcall={
|
||||
-- @field #string NORMAL Normal difficulty: error margin 5 deviation from ideal for high score and 10 for low score. No score for deviation >10.
|
||||
-- @field #string HARD Hard difficulty: error margin 2.5 deviation from ideal value for high score and 5 for low score. No score for deviation >5.
|
||||
CARRIERTRAINER.Difficulty={
|
||||
EASY="Rookey",
|
||||
EASY="Flight Student",
|
||||
NORMAL="Naval Aviator",
|
||||
HARD="TOPGUN Graduate",
|
||||
}
|
||||
|
||||
--- Groove position.
|
||||
-- @type CARRIERTRAINER.GroovePos
|
||||
-- @field #string X At the start.
|
||||
-- @field #string X0 Entering the groove.
|
||||
-- @field #string XX At the start, i.e. 3/4 from the run down.
|
||||
-- @field #string RB Roger ball.
|
||||
-- @field #string IM In the middle.
|
||||
-- @field #string IC In close.
|
||||
-- @field #string AR At the ramp.
|
||||
-- @field #string IW In the wires.
|
||||
CARRIERTRAINER.GroovePos={
|
||||
X="X",
|
||||
X0="X0",
|
||||
XX="X",
|
||||
RB="RB",
|
||||
IM="IM",
|
||||
IC="IC",
|
||||
AR="AR",
|
||||
IW="IW",
|
||||
}
|
||||
|
||||
--- Groove data.
|
||||
@ -187,18 +191,18 @@ CARRIERTRAINER.GroovePos={
|
||||
-- @field #number Alt Altitude in meters.
|
||||
-- @field #number GSE Glide slope error in degrees.
|
||||
-- @field #number LUE Lineup error in degrees.
|
||||
-- @field #number Roll Roll angle.
|
||||
|
||||
--- Player data table holding all important parameters for each player.
|
||||
--- Player data table holding all important parameters of each player.
|
||||
-- @type CARRIERTRAINER.PlayerData
|
||||
-- @field #number id Player ID.
|
||||
-- @field Wrapper.Unit#UNIT unit Aircraft unit of the player.
|
||||
-- @field Wrapper.Client#CLIENT client Client object of player.
|
||||
-- @field Wrapper.Unit#UNIT unit Aircraft of the player.
|
||||
-- @field #string callsign Callsign of player.
|
||||
-- @field #string difficulty Difficulty level.
|
||||
-- @field #number score Player score of the current pass.
|
||||
-- @field #number passes Number of passes.
|
||||
-- @field #table debrief Debrief analysis of the current step of this pass.
|
||||
-- @field #table results Results of all passes.
|
||||
-- @field Wrapper.Client#CLIENT client object of player.
|
||||
-- @field #string difficulty Difficulty level.
|
||||
-- @field #boolean inbigzone If true, player is in the big zone.
|
||||
-- @field #boolean landed If true, player landed or attempted to land.
|
||||
-- @field #boolean bolter If true, LSO told player to bolter.
|
||||
@ -206,7 +210,7 @@ CARRIERTRAINER.GroovePos={
|
||||
-- @field #boolean waveoff If true, player was waved off during final approach.
|
||||
-- @field #boolean patternwo If true, playe was waved of during the pattern.
|
||||
-- @field #number Tlso Last time the LSO gave an advice.
|
||||
-- @field #CARRIERTRAINER.GroovePos Groove data table with elemets of type @{#CARRIERTRAINER.GrooveData}.
|
||||
-- @field #CARRIERTRAINER.GroovePos groove Data table at each position in the groove. Elemets are of type @{#CARRIERTRAINER.GrooveData}.
|
||||
|
||||
--- Checkpoint parameters triggering the next step in the pattern.
|
||||
-- @type CARRIERTRAINER.Checkpoint
|
||||
@ -231,7 +235,7 @@ CARRIERTRAINER.MenuF10={}
|
||||
|
||||
--- Carrier trainer class version.
|
||||
-- @field #string version
|
||||
CARRIERTRAINER.version="0.1.6"
|
||||
CARRIERTRAINER.version="0.1.7"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@ -257,7 +261,7 @@ CARRIERTRAINER.version="0.1.6"
|
||||
-- @param #CARRIERTRAINER self
|
||||
-- @param carriername Name of the aircraft carrier unit as defined in the mission editor.
|
||||
-- @param alias (Optional) Alias for the carrier. This will be used for radio messages and the F10 radius menu. Default is the carrier name as defined in the mission editor.
|
||||
-- @return #CARRIERTRAINER self
|
||||
-- @return #CARRIERTRAINER self or nil if carrier unit does not exist.
|
||||
function CARRIERTRAINER:New(carriername, alias)
|
||||
|
||||
-- Inherit everthing from FSM class.
|
||||
@ -267,13 +271,15 @@ function CARRIERTRAINER:New(carriername, alias)
|
||||
self.carrier=UNIT:FindByName(carriername)
|
||||
|
||||
if self.carrier then
|
||||
-- Carrier zones.
|
||||
self.registerZone = ZONE_UNIT:New("registerZone", self.carrier, 2500, {dx = -5000, dy = 100, relative_to_unit=true})
|
||||
self.startZone = ZONE_UNIT:New("startZone", self.carrier, 1000, {dx = -2000, dy = 100, relative_to_unit=true})
|
||||
self.giantZone = ZONE_UNIT:New("giantZone", self.carrier, 30000, {dx = 0, dy = 0, relative_to_unit=true})
|
||||
else
|
||||
-- Carrier unit does not exist error.
|
||||
local text=string.format("ERROR: Carrier unit %s could not be found! Make sure this UNIT is defined in the mission editor and check the spelling of the unit name carefully.", carriername)
|
||||
MESSAGE:New(text, 120):ToAll()
|
||||
self:E(self.lid..text)
|
||||
self:E(text)
|
||||
return nil
|
||||
end
|
||||
|
||||
@ -411,46 +417,66 @@ function CARRIERTRAINER:_CheckPlayerStatus()
|
||||
-- Check if player was previously not inside the zone.
|
||||
if playerData.inbigzone==false then
|
||||
|
||||
-- Welcome player once he enters the carrier zone.
|
||||
local text=string.format("Welcome back, %s! TCN 74X, ICLS 1, BRC 354 (MAG HDG).\n", playerData.callsign)
|
||||
|
||||
-- Heading and distance to register for approach.
|
||||
local heading=playerData.unit:GetCoordinate():HeadingTo(self.registerZone:GetCoordinate())
|
||||
local distance=playerData.unit:GetCoordinate():Get2DDistance(self.registerZone:GetCoordinate())
|
||||
|
||||
-- Send message.
|
||||
text=text..string.format("Fly heading %d for %.1f NM and turn to BRC.", heading, distance)
|
||||
MESSAGE:New(text, 5):ToClient(playerData.client)
|
||||
|
||||
end
|
||||
|
||||
|
||||
if playerData.step==0 and unit:InAir() then
|
||||
-- New approach.
|
||||
self:_NewRound(playerData)
|
||||
-- Jump to Groove for testing.
|
||||
playerData.step=8
|
||||
|
||||
-- Jump to Groove for testing.
|
||||
if self.groovedebug then
|
||||
playerData.step=8
|
||||
self.groovedebug=false
|
||||
end
|
||||
elseif playerData.step == 1 then
|
||||
-- Entering the pattern.
|
||||
self:_Start(playerData)
|
||||
elseif playerData.step == 2 then
|
||||
-- Upwind leg.
|
||||
self:_Upwind(playerData)
|
||||
elseif playerData.step == 3 then
|
||||
-- Early break.
|
||||
self:_Break(playerData, "early")
|
||||
elseif playerData.step == 4 then
|
||||
-- Late break.
|
||||
self:_Break(playerData, "late")
|
||||
elseif playerData.step == 5 then
|
||||
-- Abeam position.
|
||||
self:_Abeam(playerData)
|
||||
elseif playerData.step == 6 then
|
||||
-- Check long down wind leg.
|
||||
if playerData.longDownwindDone==false then
|
||||
self:_CheckForLongDownwind(playerData)
|
||||
end
|
||||
self:_CheckForLongDownwind(playerData)
|
||||
-- At the ninety.
|
||||
self:_Ninety(playerData)
|
||||
elseif playerData.step==7 then
|
||||
-- In the wake.
|
||||
self:_Wake(playerData)
|
||||
elseif playerData.step==8 then
|
||||
-- Entering the groove.
|
||||
self:_Groove(playerData)
|
||||
elseif playerData.step>=90 and playerData.step<=99 then
|
||||
self:_CallTheBall(playerData)
|
||||
-- In the groove.
|
||||
self:_CallTheBall(playerData)
|
||||
elseif playerData.step==999 then
|
||||
self:_Debrief(playerData)
|
||||
-- Debriefing.
|
||||
SCHEDULER:New(nil, self._Debrief, {self,playerData}, 10)
|
||||
--SCHEDULER:New(self:_Debrief(playerData)
|
||||
playerData.step=-1
|
||||
end
|
||||
|
||||
else
|
||||
playerData.inbigzone=false
|
||||
playerData.inbigzone=false
|
||||
end
|
||||
|
||||
else
|
||||
@ -508,12 +534,10 @@ function CARRIERTRAINER:OnEventBirth(EventData)
|
||||
|
||||
-- Init player data.
|
||||
self.players[_playername]=self:_InitPlayer(_unitName)
|
||||
|
||||
-- Test
|
||||
--CARRIERTRAINER.LSOcall.HIGHL:ToGroup(_group)
|
||||
--CARRIERTRAINER.LSOcall.CALLTHEBALL:ToGroup(_group, 10)
|
||||
--MESSAGE:New(CARRIERTRAINER.LSOcall.HIGHT, 5):ToAllIf(self.Debug)
|
||||
|
||||
|
||||
-- Start in the groove for debugging.
|
||||
self.groovedebug=false
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@ -547,19 +571,27 @@ function CARRIERTRAINER:OnEventLand(EventData)
|
||||
-- Coordinate at landing event
|
||||
local coord=playerData.unit:GetCoordinate()
|
||||
|
||||
-- Debug mark of player landing coord.
|
||||
local lp=coord:MarkToAll("Landing coord.")
|
||||
coord:SmokeGreen()
|
||||
|
||||
-- Debug marks of wires.
|
||||
local w1=self.carrier:GetCoordinate():Translate(-104, 0):MarkToAll("Wire 1")
|
||||
local w2=self.carrier:GetCoordinate():Translate( -92, 0):MarkToAll("Wire 2")
|
||||
local w3=self.carrier:GetCoordinate():Translate( -80, 0):MarkToAll("Wire 3")
|
||||
local w4=self.carrier:GetCoordinate():Translate( -68, 0):MarkToAll("Wire 4")
|
||||
|
||||
-- We did land.
|
||||
playerData.landed=true
|
||||
|
||||
--TODO: maybe check that we actually landed on the right carrier.
|
||||
|
||||
-- Call trapped function in 5 seconds to make sure we did not bolter.
|
||||
SCHEDULER:New(nil, self._Trapped,{self, playerData, coord}, 5)
|
||||
-- Call trapped function in 3 seconds to make sure we did not bolter.
|
||||
SCHEDULER:New(nil, self._Trapped,{self, playerData, coord}, 3)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- CARRIER TRAINING functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -570,6 +602,7 @@ end
|
||||
-- @return #CARRIERTRAINER.PlayerData Player data.
|
||||
function CARRIERTRAINER:_InitPlayer(unitname)
|
||||
|
||||
-- Player data.
|
||||
local playerData={} --#CARRIERTRAINER.PlayerData
|
||||
|
||||
-- Player unit, client and callsign.
|
||||
@ -602,15 +635,16 @@ end
|
||||
-- @param #CARRIERTRAINER.PlayerData playerData Player data.
|
||||
-- @return #CARRIERTRAINER.PlayerData Initialized player data.
|
||||
function CARRIERTRAINER:_InitNewRound(playerData)
|
||||
self:I(self.lid..string.format("New round for player %s.", playerData.callsign))
|
||||
playerData.step=0
|
||||
playerData.score=100
|
||||
playerData.grade={}
|
||||
playerData.groove={}
|
||||
playerData.debrief={}
|
||||
playerData.longDownwindDone=false
|
||||
playerData.patternwo=false
|
||||
playerData.waveoff=false
|
||||
playerData.bolter=false
|
||||
playerData.boltered=false
|
||||
playerData.landed=false
|
||||
playerData.waveoff=false
|
||||
playerData.patternwo=false
|
||||
playerData.Tlso=timer.getTime()
|
||||
return playerData
|
||||
end
|
||||
@ -642,7 +676,7 @@ function CARRIERTRAINER:_Start(playerData)
|
||||
-- Inform player.
|
||||
local hint = string.format("Entering the pattern.")
|
||||
if playerData.difficulty==CARRIERTRAINER.Difficulty.EASY then
|
||||
hint=hint.."Aim for 800 feet and 350 kts in the break entry."
|
||||
hint=hint.."Aim for 800 feet and 350 kts at the break entry."
|
||||
end
|
||||
|
||||
-- Send message.
|
||||
@ -750,7 +784,7 @@ function CARRIERTRAINER:_CheckForLongDownwind(playerData)
|
||||
local relhead=self:_GetRelativeHeading(playerData.unit)
|
||||
|
||||
-- One NM from carrier is too far.
|
||||
local limit=-UTILS.NMToMeters(1)
|
||||
local limit=-UTILS.NMToMeters(1.5)
|
||||
|
||||
local text=string.format("Long groove check: X=%d, relhead=%.1f", X, relhead)
|
||||
self:T(text)
|
||||
@ -772,9 +806,6 @@ function CARRIERTRAINER:_CheckForLongDownwind(playerData)
|
||||
playerData.score=playerData.score-40
|
||||
|
||||
local grade="LIG PATTERN WAVE OFF - CUT 1 PT"
|
||||
|
||||
-- Long downwind done!
|
||||
playerData.longDownwindDone = true
|
||||
|
||||
-- Next step: Debriefing.
|
||||
playerData.step=999
|
||||
@ -835,7 +866,7 @@ function CARRIERTRAINER:_Ninety(playerData)
|
||||
-- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier)
|
||||
local X, Z = self:_GetDistances(playerData.unit)
|
||||
|
||||
--if(Z < -3700 or X < -3700 or X > 0) then
|
||||
-- Check abort conditions.
|
||||
if self:_CheckAbort(X, Z, self.Ninety) then
|
||||
self:_AbortPattern(playerData, X, Z, self.Ninety)
|
||||
return
|
||||
@ -866,9 +897,6 @@ function CARRIERTRAINER:_Ninety(playerData)
|
||||
-- Add to debrief.
|
||||
self:_AddToSummary(playerData, "At the 90", hintFull)
|
||||
|
||||
-- Long downwind not an issue any more
|
||||
playerData.longDownwindDone=true
|
||||
|
||||
-- Next step: wake.
|
||||
playerData.step = 7
|
||||
|
||||
@ -932,20 +960,22 @@ function CARRIERTRAINER:_Groove(playerData)
|
||||
self:_AbortPattern(playerData, X, Z, self.Groove)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call the ball distance.
|
||||
local calltheball=UTILS.NMToMeters(0.75)+math.abs(self.sterndist)
|
||||
|
||||
local relhead=self:_GetRelativeHeading(playerData.unit)+self.rwyangle
|
||||
local lineup=self:_Lineup(playerData)-self.rwyangle
|
||||
local roll=playerData.unit:GetRoll()
|
||||
|
||||
env.info(string.format("FF relhead=%d lineup=%d roll=%d", relhead, lineup, roll))
|
||||
|
||||
local calltheball=UTILS.NMToMeters(0.75)
|
||||
|
||||
if rho<=calltheball then
|
||||
if math.abs(lineup)<5 and math.abs(relhead)<10 then
|
||||
|
||||
-- Get player altitude and AoA.
|
||||
local alt = playerData.unit:GetAltitude()
|
||||
local aoa = playerData.unit:GetAoA()
|
||||
|
||||
|
||||
self:_SendMessageToPlayer("Call the ball.", 8, playerData)
|
||||
CARRIERTRAINER.LSOcall.CALLTHEBALL:ToGroup(playerData.unit:GetGroup())
|
||||
|
||||
-- Grade altitude.
|
||||
local hintAlt=self:_AltitudeCheck(playerData, self.Groove, alt)
|
||||
|
||||
@ -959,22 +989,20 @@ function CARRIERTRAINER:_Groove(playerData)
|
||||
self:_SendMessageToPlayer(hintFull, 10, playerData)
|
||||
|
||||
-- Add to debrief.
|
||||
self:_AddToSummary(playerData, "Calling the ball", hintFull)
|
||||
self:_AddToSummary(playerData, "Enter Groove", hintFull)
|
||||
|
||||
-- Gather pilot data.
|
||||
local groovedata={} --#CARRIERTRAINER.GrooveData
|
||||
groovedata.Alt=alt
|
||||
groovedata.AoA=aoa
|
||||
groovedata.GSE=self:_Glideslope(playerData)-3.5
|
||||
groovedata.LUE=self:_Lineup(playerData)-10
|
||||
groovedata.LUE=self:_Lineup(playerData)-self.rwyangle
|
||||
groovedata.Step=playerData.step
|
||||
|
||||
-- Init groove table.
|
||||
playerData.Groove={}
|
||||
-- Groove
|
||||
playerData.groove.X0=groovedata
|
||||
|
||||
|
||||
playerData.Groove.X=groovedata
|
||||
|
||||
-- Next step: roger ball.
|
||||
-- Next step: X call the ball.
|
||||
playerData.step=90
|
||||
end
|
||||
|
||||
@ -1013,51 +1041,63 @@ function CARRIERTRAINER:_CallTheBall(playerData)
|
||||
local AoA=playerData.unit:GetAoA()
|
||||
|
||||
-- Ranges in the groove.
|
||||
local RRB=UTILS.NMToMeters(0.500) -- Roger Ball! call.
|
||||
local RIM=UTILS.NMToMeters(0.375) -- In the Middle 0.75/2.
|
||||
local RIC=UTILS.NMToMeters(0.100) -- In Close.
|
||||
local RAR=UTILS.NMToMeters(0.050) -- At the Ramp.
|
||||
local RXX=UTILS.NMToMeters(0.750)+math.abs(self.sterndist) -- Start of groove. 0.75 = 1389 m
|
||||
local RRB=UTILS.NMToMeters(0.500)+math.abs(self.sterndist) -- Roger Ball! call. 0.5 = 926 m
|
||||
local RIM=UTILS.NMToMeters(0.375)+math.abs(self.sterndist) -- In the Middle 0.75/2. 0.375 = 695 m
|
||||
local RIC=UTILS.NMToMeters(0.100)+math.abs(self.sterndist) -- In Close. 0.1 = 185 m
|
||||
local RAR=UTILS.NMToMeters(0.000)+math.abs(self.sterndist) -- At the Ramp.
|
||||
|
||||
-- Data
|
||||
local groovedata={} --#CARRIERTRAINER.GrooveData
|
||||
groovedata.Step=playerData.step
|
||||
groovedata.Alt=alt
|
||||
groovedata.AoA=AoA
|
||||
groovedata.GSE=glideslopeError
|
||||
groovedata.LUE=lineupError
|
||||
groovedata.Step=playerData.step
|
||||
|
||||
if rho<=RRB and playerData.step==90 then
|
||||
if rho<=RXX and playerData.step==90 then
|
||||
|
||||
-- LSO "Call the ball" call.
|
||||
self:_SendMessageToPlayer("Call the ball.", 8, playerData)
|
||||
CARRIERTRAINER.LSOcall.CALLTHEBALL:ToGroup(playerData.unit:GetGroup())
|
||||
playerData.Tlso=timer.getTime()
|
||||
|
||||
-- Next step: roger ball.
|
||||
playerData.step=91
|
||||
|
||||
elseif rho<=RRB and playerData.step==91 then
|
||||
|
||||
-- Roger ball!
|
||||
-- Pilot: "Roger ball" call.
|
||||
self:_SendMessageToPlayer(CARRIERTRAINER.LSOcall.ROGERBALLT, 8, playerData)
|
||||
CARRIERTRAINER.LSOcall.ROGERBALL:ToGroup(player)
|
||||
playerData.Tlso=timer.getTime()+1
|
||||
|
||||
-- Store data.
|
||||
playerData.Groove.RB=groovedata
|
||||
playerData.groove.RB=groovedata
|
||||
|
||||
-- Next step: in the middle.
|
||||
playerData.step=91
|
||||
playerData.step=92
|
||||
|
||||
elseif rho<=RIM and playerData.step==91 then
|
||||
elseif rho<=RIM and playerData.step==92 then
|
||||
|
||||
--TODO: grade for IM
|
||||
self:_SendMessageToPlayer("IM", 8, playerData)
|
||||
env.info(string.format("FF IM=%d", rho))
|
||||
|
||||
-- Store data.
|
||||
playerData.Groove.IM=groovedata
|
||||
playerData.groove.IM=groovedata
|
||||
|
||||
-- Next step: in close.
|
||||
playerData.step=92
|
||||
playerData.step=93
|
||||
|
||||
elseif rho<=RIC and playerData.step==92 then
|
||||
elseif rho<=RIC and playerData.step==93 then
|
||||
|
||||
--TODO: grade for IC, call wave off?
|
||||
self:_SendMessageToPlayer("IC", 8, playerData)
|
||||
env.info(string.format("FF IC=%d", rho))
|
||||
|
||||
-- Store data.
|
||||
playerData.Groove.IC=groovedata
|
||||
playerData.groove.IC=groovedata
|
||||
|
||||
-- Check if player should wave off.
|
||||
local waveoff=self:_CheckWaveOff(glideslopeError, lineupError, AoA)
|
||||
@ -1068,26 +1108,28 @@ function CARRIERTRAINER:_CallTheBall(playerData)
|
||||
-- Wave off player.
|
||||
self:_SendMessageToPlayer(CARRIERTRAINER.LSOcall.WAVEOFFT, 10, playerData)
|
||||
CARRIERTRAINER.LSOcall.WAVEOFF:ToGroup(playerData.unit:GetGroup())
|
||||
playerData.Tlso=timer.getTime()
|
||||
|
||||
-- Next step: debrief.
|
||||
playerData.step=999
|
||||
|
||||
return
|
||||
else
|
||||
-- Next step: at the ramp.
|
||||
playerData.step=93
|
||||
playerData.step=94
|
||||
end
|
||||
|
||||
|
||||
elseif rho<=RAR and playerData.step==93 then
|
||||
elseif rho<=RAR and playerData.step==94 then
|
||||
|
||||
--TODO: grade for AR
|
||||
self:_SendMessageToPlayer("AR", 8, playerData)
|
||||
env.info(string.format("FF AR=%d", rho))
|
||||
|
||||
-- Store data.
|
||||
playerData.Groove.AR=groovedata
|
||||
playerData.groove.AR=groovedata
|
||||
|
||||
-- Next step: at the ramp.
|
||||
playerData.step=94
|
||||
playerData.step=95
|
||||
end
|
||||
|
||||
-- Time since last LSO call.
|
||||
@ -1095,31 +1137,35 @@ function CARRIERTRAINER:_CallTheBall(playerData)
|
||||
local deltaT=time-playerData.Tlso
|
||||
|
||||
-- Check if we are beween 3/4 NM and end of ship.
|
||||
if rho<UTILS.NMToMeters(0.75) and deltaT>=3 then
|
||||
if rho>=RAR and rho<RXX and deltaT>=3 then
|
||||
|
||||
-- LSO call if necessary.
|
||||
self:_LSOcall(playerData, glideslopeError, lineupError)
|
||||
|
||||
elseif X>0 then
|
||||
|
||||
local wire = 0
|
||||
local hint = ""
|
||||
local score = 0
|
||||
|
||||
|
||||
|
||||
if playerData.landed then
|
||||
hint = "You boltered."
|
||||
|
||||
local hint="You boltered."
|
||||
|
||||
-- Send message to player.
|
||||
self:_SendMessageToPlayer(hint, 8, playerData)
|
||||
|
||||
-- Add to debrief.
|
||||
self:_AddToSummary(playerData, "Bolter", hint)
|
||||
|
||||
else
|
||||
hint = "You were waved off."
|
||||
wire = -1
|
||||
score = -10
|
||||
|
||||
local hint="You were waved off."
|
||||
|
||||
-- Send message to player.
|
||||
self:_SendMessageToPlayer(hint, 8, playerData)
|
||||
|
||||
-- Add to debrief.
|
||||
self:_AddToSummary(playerData, "Wave Off", hint)
|
||||
|
||||
end
|
||||
|
||||
-- Send message to player.
|
||||
self:_SendMessageToPlayer(hint, 8, playerData)
|
||||
|
||||
-- Add to debrief.
|
||||
self:_AddToSummary(playerData, "Bolter or wave off", hint)
|
||||
|
||||
|
||||
-- Next step: debrief.
|
||||
playerData.step=999
|
||||
end
|
||||
@ -1135,14 +1181,17 @@ function CARRIERTRAINER:_CheckWaveOff(glideslopeError, lineupError, AoA)
|
||||
|
||||
local waveoff=false
|
||||
|
||||
-- Too high or too low?
|
||||
if math.abs(glideslopeError)>3 then
|
||||
waveoff=true
|
||||
end
|
||||
|
||||
-- Too far from centerline?
|
||||
if math.abs(lineupError)>3 then
|
||||
waveoff=true
|
||||
end
|
||||
|
||||
-- Too slow or too fast?
|
||||
if AoA<6.9 or AoA>9.3 then
|
||||
waveoff=true
|
||||
end
|
||||
@ -1188,25 +1237,26 @@ function CARRIERTRAINER:_Trapped(playerData, pos)
|
||||
local wire = 1
|
||||
local score = -10
|
||||
|
||||
-- Which wire
|
||||
if X<-14 then
|
||||
wire = 1
|
||||
score = -15
|
||||
elseif X<-3 then
|
||||
wire = 2
|
||||
score = 10
|
||||
elseif X<10 then
|
||||
wire = 3
|
||||
score = 20
|
||||
else
|
||||
wire = 4
|
||||
score = 7
|
||||
end
|
||||
-- Little offset for the exact wire positions.
|
||||
local wdx=11
|
||||
|
||||
-- Which wire was caught?
|
||||
if X<-104+wdx then
|
||||
wire=1
|
||||
elseif X<-92+wdx then
|
||||
wire=2
|
||||
elseif X<-80+wdx then
|
||||
wire=3
|
||||
elseif X<68+wdx then
|
||||
wire=4
|
||||
else
|
||||
wire=0
|
||||
end
|
||||
|
||||
local text=string.format("TRAPPED! %d-wire.", wire)
|
||||
self:_SendMessageToPlayer(text, 30, playerData)
|
||||
|
||||
local text2=string.format("Distance %.1f meters resulted in a %d-wire estimate.", X, wire)
|
||||
local text2=string.format("Distance X=%.1f meters resulted in a %d-wire estimate.", X, wire)
|
||||
MESSAGE:New(text,30):ToAllIf(self.Debug)
|
||||
env.info(text2)
|
||||
|
||||
@ -1217,6 +1267,9 @@ function CARRIERTRAINER:_Trapped(playerData, pos)
|
||||
--Boltered!
|
||||
playerData.boltered=true
|
||||
end
|
||||
|
||||
-- Next step: debriefing.
|
||||
playerData.step=999
|
||||
end
|
||||
|
||||
--- Entering the Groove.
|
||||
@ -1371,7 +1424,7 @@ function CARRIERTRAINER:_Debrief(playerData)
|
||||
|
||||
-- Debriefing text.
|
||||
local text=string.format("Debriefing:\n")
|
||||
text=text..string.format("===========\n\n")
|
||||
text=text..string.format("===================================================\n")
|
||||
for _,_data in pairs(playerData.debrief) do
|
||||
local step=_data.step
|
||||
local comment=_data.hint
|
||||
@ -1618,7 +1671,20 @@ end
|
||||
-- @param #CARRIERTRAINER self
|
||||
function CARRIERTRAINER:_InitStennis()
|
||||
|
||||
-- Carrier Parameters.
|
||||
self.rwyangle = -10
|
||||
self.sterndist =-150
|
||||
self.deckheight = 22
|
||||
|
||||
--[[
|
||||
q0=self.carrier:GetCoordinate():SetAltitude(25)
|
||||
q0:BigSmokeSmall(0.1)
|
||||
q1=self.carrier:GetCoordinate():Translate(-104,0):SetAltitude(22) --1st wire
|
||||
q1:BigSmokeSmall(0.1)--:SmokeGreen()
|
||||
q2=self.carrier:GetCoordinate():Translate(-68,0):SetAltitude(22) --4th wire ==> distance between wires 12 m
|
||||
q2:BigSmokeSmall(0.1)--:SmokeBlue()
|
||||
]]
|
||||
|
||||
-- Upwind leg
|
||||
self.Upwind.name="Upwind"
|
||||
self.Upwind.Xmin=-4000 -- TODO Should be withing 4 km behind carrier. Why?
|
||||
@ -1776,7 +1842,7 @@ function CARRIERTRAINER:_LSOgrade(playerData)
|
||||
|
||||
elseif playerData.landed then
|
||||
|
||||
local gdata=playerData.Groove.X --#CARRIERTRAINER.GrooveData
|
||||
local gdata=playerData.groove.X --#CARRIERTRAINER.GrooveData
|
||||
|
||||
else
|
||||
|
||||
@ -1947,7 +2013,9 @@ function CARRIERTRAINER:_SendMessageToPlayer(message, duration, playerData, clea
|
||||
if playerData.client then
|
||||
--MESSAGE:New(string.format("%s, %s, ", self.alias, playerData.callsign)..message, duration, nil, clear):ToClient(playerData.client)
|
||||
end
|
||||
MESSAGE:New(string.format("%s, %s, %s", self.alias, playerData.callsign, message), duration, nil, clear):ToAll()
|
||||
local text=string.format("%s, %s, %s", self.alias, playerData.callsign, message)
|
||||
MESSAGE:New(text, duration, nil, clear):ToAll()
|
||||
env.info(text)
|
||||
end
|
||||
|
||||
--- Display final score.
|
||||
|
||||
@ -276,7 +276,7 @@ RANGE.id="RANGE | "
|
||||
|
||||
--- Range script version.
|
||||
-- @field #string version
|
||||
RANGE.version="1.2.2"
|
||||
RANGE.version="1.2.3"
|
||||
|
||||
--TODO list:
|
||||
--TODO: Add custom weapons, which can be specified by the user.
|
||||
@ -460,9 +460,10 @@ function RANGE:SetBombtrackThreshold(distance)
|
||||
self.BombtrackThreshold=distance*1000 or 25*1000
|
||||
end
|
||||
|
||||
--- Set range location. If this is not done, one (random) unit position of the range is used to determine the center of the range.
|
||||
--- Set range location. If this is not done, one (random) unit position of the range is used to determine the location of the range.
|
||||
-- The range location determines the position at which the weather data is evaluated.
|
||||
-- @param #RANGE self
|
||||
-- @param Core.Point#COORDINATE coordinate Coordinate of the center of the range.
|
||||
-- @param Core.Point#COORDINATE coordinate Coordinate of the range.
|
||||
function RANGE:SetRangeLocation(coordinate)
|
||||
self.location=coordinate
|
||||
end
|
||||
@ -471,7 +472,7 @@ end
|
||||
-- If a zone is not explicitly specified, the range zone is determined by its location and radius.
|
||||
-- @param #RANGE self
|
||||
-- @param Core.Zone#ZONE zone MOOSE zone defining the range perimeters.
|
||||
function RANGE:SetRangeLocation(zone)
|
||||
function RANGE:SetRangeZone(zone)
|
||||
self.rangezone=zone
|
||||
end
|
||||
|
||||
@ -1161,15 +1162,21 @@ function RANGE:OnEventShot(EventData)
|
||||
local _callsign=self:_myname(_unitName)
|
||||
|
||||
-- Coordinate of impact point.
|
||||
local impactcoord=COORDINATE:NewFromVec3(_lastBombPos)
|
||||
local impactcoord=COORDINATE:NewFromVec3(_lastBombPos)
|
||||
|
||||
-- Check if impact happend in range zone.
|
||||
local insidezone=self.rangezone:IsCoordinateInZone(impactcoord)
|
||||
|
||||
-- Distance from range. We dont want to smoke targets outside of the range.
|
||||
local impactdist=impactcoord:Get2DDistance(self.location)
|
||||
|
||||
--impactcoord:MarkToAll("Bomb impact point")
|
||||
-- Impact point of bomb.
|
||||
if self.Debug then
|
||||
impactcoord:MarkToAll("Bomb impact point")
|
||||
end
|
||||
|
||||
-- Smoke impact point of bomb.
|
||||
if self.PlayerSettings[_playername].smokebombimpact and impactdist<self.rangeradius then
|
||||
if self.PlayerSettings[_playername].smokebombimpact and insidezone then
|
||||
if self.PlayerSettings[_playername].delaysmoke then
|
||||
timer.scheduleFunction(self._DelayedSmoke, {coord=impactcoord, color=self.PlayerSettings[_playername].smokecolor}, timer.getTime() + self.TdelaySmoke)
|
||||
else
|
||||
@ -1207,7 +1214,7 @@ function RANGE:OnEventShot(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
-- Count if bomb fell less than 1 km away from the target.
|
||||
-- Count if bomb fell less than ~1 km away from the target.
|
||||
if _distance <= self.scorebombdistance then
|
||||
|
||||
-- Init bomb player results.
|
||||
@ -1226,7 +1233,7 @@ function RANGE:OnEventShot(EventData)
|
||||
|
||||
-- Send message.
|
||||
self:_DisplayMessageToGroup(_unit, _message, nil, true)
|
||||
elseif _distance <= self.rangeradius then
|
||||
elseif insidezone then
|
||||
-- Send message
|
||||
local _message=string.format("%s, weapon fell more than %.1f km away from nearest range target. No score!", _callsign, self.scorebombdistance/1000)
|
||||
self:_DisplayMessageToGroup(_unit, _message, nil, false)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user