CT v0.1..8

This commit is contained in:
Frank 2018-11-06 00:04:52 +01:00
parent ba4caf179a
commit 9fac65f31f

View File

@ -66,16 +66,16 @@ CARRIERTRAINER = {
registerZone = nil,
startZone = nil,
giantZone = nil,
players = {},
menuadded = {},
Upwind = {},
Abeam = {},
BreakEarly = {},
BreakLate = {},
Ninety = {},
Wake = {},
Groove = {},
Trap = {},
players = {},
menuadded = {},
Upwind = {},
Abeam = {},
BreakEarly = {},
BreakLate = {},
Ninety = {},
Wake = {},
Groove = {},
Trap = {},
TACAN = nil,
ICLS = nil,
rwyangle = -10,
@ -193,6 +193,12 @@ CARRIERTRAINER.GroovePos={
-- @field #number LUE Lineup error in degrees.
-- @field #number Roll Roll angle.
--- LSO grade
-- @type CARRIERDATA.LSOgrade
-- @field #string grade LSO grade string
-- @field #string details Detailed step analysis.
-- @field #number points Points received.
--- Player data table holding all important parameters of each player.
-- @type CARRIERTRAINER.PlayerData
-- @field Wrapper.Client#CLIENT client Client object of player.
@ -201,8 +207,9 @@ CARRIERTRAINER.GroovePos={
-- @field #string difficulty Difficulty level.
-- @field #number score Player score of the current pass.
-- @field #number passes Number of passes.
-- @field #boolan attitudemonitor If true, display aircraft attitude and other parameters constantly.
-- @field #table debrief Debrief analysis of the current step of this pass.
-- @field #table results Results of all passes.
-- @field #table grade LSO grade of passes.
-- @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.
@ -235,7 +242,7 @@ CARRIERTRAINER.MenuF10={}
--- Carrier trainer class version.
-- @field #string version
CARRIERTRAINER.version="0.1.7w"
CARRIERTRAINER.version="0.1.8"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -409,8 +416,10 @@ function CARRIERTRAINER:_CheckPlayerStatus()
if unit:IsAlive() then
--self:_SendMessageToPlayer("current step "..self:_StepName(playerData.step),1,playerData)
--self:_DetailedPlayerStatus(playerData)
-- Display aircraft attitude and other parameters as message text.
if playerData.attitudemonitor then
self:_DetailedPlayerStatus(playerData)
end
if unit:IsInZone(self.giantZone) then
@ -436,7 +445,7 @@ function CARRIERTRAINER:_CheckPlayerStatus()
-- Jump to Groove for testing.
if self.groovedebug then
playerData.step=8
playerData.step=90
self.groovedebug=false
end
elseif playerData.step == 1 then
@ -462,16 +471,15 @@ function CARRIERTRAINER:_CheckPlayerStatus()
elseif playerData.step==7 then
-- In the wake.
self:_Wake(playerData)
elseif playerData.step==8 then
elseif playerData.step==90 then
-- Entering the groove.
self:_Groove(playerData)
elseif playerData.step>=90 and playerData.step<=99 then
elseif playerData.step>=91 and playerData.step<=99 then
-- In the groove.
self:_CallTheBall(playerData)
elseif playerData.step==999 then
-- Debriefing.
SCHEDULER:New(nil, self._Debrief, {self,playerData}, 10)
--SCHEDULER:New(self:_Debrief(playerData)
playerData.step=-1
end
@ -605,18 +613,19 @@ function CARRIERTRAINER:_InitPlayer(unitname)
-- Player data.
local playerData={} --#CARRIERTRAINER.PlayerData
-- Player unit, client and callsign.
-- Player unit, client and callsign.
playerData.unit = UNIT:FindByName(unitname)
playerData.client = CLIENT:FindByName(unitname, nil, true)
playerData.callsign = playerData.unit:GetCallsign()
-- Total score of player.
playerData.totalscore = playerData.totalscore or 0
-- Number of passes done by player.
playerData.passes=playerData.passes or 0
playerData.results=playerData.results or {}
-- LSO grades.
playerData.grades=playerData.grades or {}
-- Attitude monitor.
playerData.attitudemonitor=false
-- Set difficulty level.
playerData.difficulty=playerData.difficulty or CARRIERTRAINER.Difficulty.NORMAL
@ -709,13 +718,13 @@ function CARRIERTRAINER:_Upwind(playerData)
local altitude=playerData.unit:GetAltitude()
-- Get altitude.
local hint=self:_AltitudeCheck(playerData, self.Upwind, altitude)
local hint, debrief=self:_AltitudeCheck(playerData, self.Upwind, altitude)
-- Message to player
self:_SendMessageToPlayer(hint, 8, playerData)
self:_SendMessageToPlayer(hint, 10, playerData)
-- Debrief.
self:_AddToSummary(playerData, "Entering the Break", hint)
self:_AddToSummary(playerData, "Entering the Break", debrief)
-- Next step.
playerData.step=3
@ -751,16 +760,16 @@ function CARRIERTRAINER:_Break(playerData, part)
local altitude=playerData.unit:GetAltitude()
-- Grade altitude.
local hint=self:_AltitudeCheck(playerData, breakpoint, altitude)
local hint, debrief=self:_AltitudeCheck(playerData, breakpoint, altitude)
-- Send message to player.
self:_SendMessageToPlayer(hint, 10, playerData)
-- Debrief
if part=="late" then
self:_AddToSummary(playerData, "Late Break", hint)
self:_AddToSummary(playerData, "Late Break", debrief)
else
self:_AddToSummary(playerData, "Early Break", hint)
self:_AddToSummary(playerData, "Early Break", debrief)
end
-- Next step: late break or abeam.
@ -800,10 +809,7 @@ function CARRIERTRAINER:_CheckForLongDownwind(playerData)
CARRIERTRAINER.LSOcall.LONGGROOVE:ToGroup(playerData.unit:GetGroup())
-- Debrief.
self:_AddToSummary(playerData, "Long in the groove", "bla")
-- Decrease score.
playerData.score=playerData.score-40
self:_AddToSummary(playerData, "Downwind", "Long in the groove.")
local grade="LIG PATTERN WAVE OFF - CUT 1 PT"
@ -835,26 +841,27 @@ function CARRIERTRAINER:_Abeam(playerData)
local aoa = playerData.unit:GetAoA()
local alt = playerData.unit:GetAltitude()
-- Grade AoA.
local hintAoA=self:_AoACheck(playerData, self.Abeam, aoa)
-- Grade Altitude.
local hintAlt=self:_AltitudeCheck(playerData, self.Abeam, alt)
local hintAlt, debriefAlt=self:_AltitudeCheck(playerData, self.Abeam, alt)
-- Grade AoA.
local hintAoA, debriefAoA=self:_AoACheck(playerData, self.Abeam, aoa)
-- Grade distance to carrier.
local hintDist=self:_DistanceCheck(playerData, self.Abeam, math.abs(Z))
local hintDist, debriefDist=self:_DistanceCheck(playerData, self.Abeam, math.abs(Z))
-- Compile full hint.
local hintFull=string.format("%s\n%s\n%s", hintAlt, hintAoA, hintDist)
local hint=string.format("%s\n%s\n%s", hintAlt, hintAoA, hintDist)
local debrief=string.format("%s\n%s\n%s", debriefAlt, debriefAoA, debriefDist)
-- Send message to playerr.
self:_SendMessageToPlayer(hintFull, 10, playerData)
self:_SendMessageToPlayer(hint, 10, playerData)
-- Add to debrief.
self:_AddToSummary(playerData, "Abeam Position", hintFull)
self:_AddToSummary(playerData, "Abeam Position", debrief)
-- Next step: ninety.
playerData.step = 6
playerData.step=6
end
end
@ -883,22 +890,23 @@ function CARRIERTRAINER:_Ninety(playerData)
local aoa=playerData.unit:GetAoA()
-- Grade altitude.
local hintAlt=self:_AltitudeCheck(playerData, self.Ninety, alt)
local hintAlt, debriefAlt=self:_AltitudeCheck(playerData, self.Ninety, alt)
-- Grade AoA.
local hintAoA=self:_AoACheck(playerData, self.Ninety, aoa)
local hintAoA, debriefAoA=self:_AoACheck(playerData, self.Ninety, aoa)
-- Compile full hint.
local hintFull=string.format("%s\n%s", hintAlt, hintAoA)
local hint=string.format("%s\n%s", hintAlt, hintAoA)
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Message to player.
self:_SendMessageToPlayer(hintFull, 10, playerData)
self:_SendMessageToPlayer(hint, 10, playerData)
-- Add to debrief.
self:_AddToSummary(playerData, "At the 90", hintFull)
self:_AddToSummary(playerData, "At the 90", debrief)
-- Next step: wake.
playerData.step = 7
playerData.step=7
elseif relheading>90 and self:_CheckLimits(X, Z, self.Wake) then
-- Message to player.
@ -928,22 +936,23 @@ function CARRIERTRAINER:_Wake(playerData)
local aoa=playerData.unit:GetAoA()
-- Grade altitude.
local hintAlt=self:_AltitudeCheck(playerData, self.Wake, alt)
local hintAlt, debriefAlt=self:_AltitudeCheck(playerData, self.Wake, alt)
-- Grade AoA.
local hintAoA=self:_AoACheck(playerData, self.Wake, aoa)
local hintAoA, debriefAoA=self:_AoACheck(playerData, self.Wake, aoa)
-- Compile full hint.
local hintFull=string.format("%s\n%s", hintAlt, hintAoA)
local hint=string.format("%s\n%s", hintAlt, hintAoA)
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Message to player.
self:_SendMessageToPlayer(hintFull, 10, playerData)
self:_SendMessageToPlayer(hint, 10, playerData)
-- Add to debrief.
self:_AddToSummary(playerData, "At the Wake", hintFull)
self:_AddToSummary(playerData, "At the Wake", debrief)
-- Next step: Groove.
playerData.step = 8
playerData.step=90
end
end
@ -960,9 +969,6 @@ 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
@ -977,19 +983,20 @@ function CARRIERTRAINER:_Groove(playerData)
local aoa = playerData.unit:GetAoA()
-- Grade altitude.
local hintAlt=self:_AltitudeCheck(playerData, self.Groove, alt)
local hintAlt, debriefAlt=self:_AltitudeCheck(playerData, self.Groove, alt)
-- AoA feed back
local hintAoA=self:_AoACheck(playerData, self.Groove, aoa)
local hintAoA, debriefAoA=self:_AoACheck(playerData, self.Groove, aoa)
-- Compile full hint.
local hintFull=string.format("%s\n%s", hintAlt, hintAoA)
local hint=string.format("%s\n%s", hintAlt, hintAoA)
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Message to player.
self:_SendMessageToPlayer(hintFull, 10, playerData)
self:_SendMessageToPlayer(hint, 10, playerData)
-- Add to debrief.
self:_AddToSummary(playerData, "Enter Groove", hintFull)
self:_AddToSummary(playerData, "Enter Groove", debrief)
-- Gather pilot data.
local groovedata={} --#CARRIERTRAINER.GrooveData
@ -997,13 +1004,14 @@ function CARRIERTRAINER:_Groove(playerData)
groovedata.AoA=aoa
groovedata.GSE=self:_Glideslope(playerData)-3.5
groovedata.LUE=self:_Lineup(playerData)-self.rwyangle
groovedata.Roll=roll
groovedata.Step=playerData.step
-- Groove
playerData.groove.X0=groovedata
-- Next step: X call the ball.
playerData.step=90
playerData.step=91
end
end
@ -1054,21 +1062,22 @@ function CARRIERTRAINER:_CallTheBall(playerData)
groovedata.AoA=AoA
groovedata.GSE=glideslopeError
groovedata.LUE=lineupError
groovedata.Roll=playerData.unit:GetRoll()
if rho<=RXX and playerData.step==90 then
if rho<=RXX and playerData.step==91 then
-- LSO "Call the ball" call.
self:_SendMessageToPlayer("Call the ball.", 8, playerData)
CARRIERTRAINER.LSOcall.CALLTHEBALL:ToGroup(playerData.unit:GetGroup())
playerData.Tlso=timer.getTime()
-- Store data.
playerData.groove.XX=groovedata
-- Next step: roger ball.
playerData.step=91
-- Store data.
playerData.groove.XX=groovedata
playerData.step=92
elseif rho<=RRB and playerData.step==91 then
elseif rho<=RRB and playerData.step==92 then
-- Pilot: "Roger ball" call.
self:_SendMessageToPlayer(CARRIERTRAINER.LSOcall.ROGERBALLT, 8, playerData)
@ -1079,9 +1088,9 @@ function CARRIERTRAINER:_CallTheBall(playerData)
playerData.groove.RB=groovedata
-- Next step: in the middle.
playerData.step=92
playerData.step=93
elseif rho<=RIM and playerData.step==92 then
elseif rho<=RIM and playerData.step==93 then
--TODO: grade for IM
self:_SendMessageToPlayer("IM", 8, playerData)
@ -1091,9 +1100,9 @@ function CARRIERTRAINER:_CallTheBall(playerData)
playerData.groove.IM=groovedata
-- Next step: in close.
playerData.step=93
playerData.step=94
elseif rho<=RIC and playerData.step==93 then
elseif rho<=RIC and playerData.step==94 then
--TODO: grade for IC, call wave off?
self:_SendMessageToPlayer("IC", 8, playerData)
@ -1119,10 +1128,10 @@ function CARRIERTRAINER:_CallTheBall(playerData)
return
else
-- Next step: at the ramp.
playerData.step=94
playerData.step=95
end
elseif rho<=RAR and playerData.step==94 then
elseif rho<=RAR and playerData.step==95 then
--TODO: grade for AR
self:_SendMessageToPlayer("AR", 8, playerData)
@ -1132,7 +1141,7 @@ function CARRIERTRAINER:_CallTheBall(playerData)
playerData.groove.AR=groovedata
-- Next step: in the wires.
playerData.step=95
playerData.step=96
end
-- Time since last LSO call.
@ -1375,7 +1384,7 @@ function CARRIERTRAINER:_Glideslope(playerData)
-- Glideslope. Wee need to correct for the height of the deck. The ideal glide slope is 3.5 degrees.
local h=playerData.unit:GetAltitude()-self.deckheight
local x=math.abs(X-self.sterndist)
local x=math.abs(X-self.sterndist) --TODO: maybe sterndist should be replaced by position of 3-wire!
local glideslope=math.atan(h/x)
return math.deg(glideslope)
@ -1443,11 +1452,15 @@ function CARRIERTRAINER:_Debrief(playerData)
end
-- Send debrief message to player
self:_SendMessageToPlayer(text, 60, playerData, true)
--TODO: add final grades, memorize score deductions.
--self:_PrintFinalScore(playerData, 30, -2)
--self:_HandleCollectedResult(playerData, -2)
self:_SendMessageToPlayer(text, 30, playerData, true)
-- New approach.
if playerData.boltered or playerData.waveoff or playerData.patternwo then
local heading=playerData.unit:GetCoordinate():HeadingTo(self.registerZone:GetCoordinate())
local distance=playerData.unit:GetCoordinate():Get2DDistance(self.registerZone:GetCoordinate())
local text=string.format("fly heading %d for %d NM to restart the pattern.", heading, UTILS.MetersToNM(distance))
self:_SendMessageToPlayer(text, 10, playerData)
end
-- Next step.
playerData.step=0
@ -1493,9 +1506,21 @@ function CARRIERTRAINER:_StepName(step)
elseif step==7 then
name="at the wake"
elseif step==8 then
name="in the groove"
elseif step==9 then
name="trapped"
name="unkown"
elseif step==90 then
name="X0: Entering the Groove"
elseif step==91 then
name="X: At the Start"
elseif step==92 then
name="Roger Ball"
elseif step==93 then
name="IM: In the Middle"
elseif step==94 then
name="IC: In Close"
elseif step==95 then
name="AR: At the Ramp"
elseif step==96 then
name="IW: In the Wires"
end
return name
@ -1562,17 +1587,6 @@ function CARRIERTRAINER:_CheckAbort(X, Z, pos)
abort=true
end
--[[
-- Abort conditions.
local abortXmin=check.Xmin and (check.Xmin<0 and X<=check.Xmin or check.Xmin>=0 and X>=check.Xmin)
local abortXmax=check.Xmax and (check.Xmax<0 and X>=check.Xmax or check.Xmax>=0 and X<=check.Xmax)
local abortZmin=check.Zmin and (check.Zmin<0 and Z<=check.Zmin or check.Zmin>=0 and Z>=check.Zmin)
local abortZmax=check.Zmax and (check.Zmax<0 and Z>=check.Zmax or check.Zmax>=0 and Z<=check.Zmax)
-- Check if any of the conditions are met.
local abort=abortXmin or abortXmax or abortZmin or abortZmax
]]
return abort
end
@ -1665,13 +1679,20 @@ function CARRIERTRAINER:_DetailedPlayerStatus(playerData)
local relhead=self:_GetRelativeHeading(playerData.unit)
local text=string.format("%s, current step: %.1f\n", playerData.callsign, self:_StepName(playerData.step))
text=text..string.format("AoA=%.1f | Vx=%.1f Vy=%.1f Vz=%.1f\n", aoa, velo.x, velo.y, velo.z)
text=text..string.format("Wind Vx=%.1f Vy=%.1f Vz=%.1f\n", wind.x, wind.y, wind.z)
text=text..string.format("pitch=%.1f | roll=%.1f | yaw=%.1f | climb=%.1f\n", pitch, roll, yaw, unit:GetClimbAngle())
text=text..string.format("relheading=%.1f degrees\n", relhead)
text=text..string.format("Distance: X=%d m Z=%d m", dx, dz)
text=text..string.format("rho=%.1f m phi=%.1f degrees\n", rho,phi)
local text=string.format("AoA=%.1f | Vx=%.1f Vy=%.1f Vz=%.1f\n", aoa, velo.x, velo.y, velo.z)
text=text..string.format("Pitch=%.1f° | Roll=%.1f° | Yaw=%.1f° | Climb=%.1f°\n", pitch, roll, yaw, unit:GetClimbAngle())
text=text..string.format("Relheading=%.1f°\n", relhead)
text=text..string.format("Distance: X=%d m Z=%d m\n", dx, dz)
if playerData.step>=90 and playerData.step<=99 then
local lineup=self:_Lineup(playerData)-self.rwyangle
local glideslope=self:_Glideslope(playerData)-3.5
text=text..string.format("Lineup Error = %.1f°\n", lineup)
text=text..string.format("Glideslope Error = %.1f°\n", glideslope)
end
text=text..string.format("Current step: %s\n", self:_StepName(playerData.step))
--text=text..string.format("Wind Vx=%.1f Vy=%.1f Vz=%.1f\n", wind.x, wind.y, wind.z)
--text=text..string.format("rho=%.1f m phi=%.1f degrees\n", rho,phi)
MESSAGE:New(text, 1, nil , true):ToClient(playerData.client)
end
@ -1711,7 +1732,7 @@ function CARRIERTRAINER:_InitStennis()
-- Early break
self.BreakEarly.name="Early Break"
self.BreakEarly.Xmin=-500
self.BreakEarly.Xmax=4000
self.BreakEarly.Xmax=UTILS.NMToMeters(5)
self.BreakEarly.Zmin=-3700
self.BreakEarly.Zmax=1500
self.BreakEarly.LimitXmin=0
@ -1725,7 +1746,7 @@ function CARRIERTRAINER:_InitStennis()
-- Late break
self.BreakLate.name="Late Break"
self.BreakLate.Xmin=-500
self.BreakLate.Xmax=4000
self.BreakLate.Xmax=UTILS.NMToMeters(5)
self.BreakLate.Zmin=-3700
self.BreakLate.Zmax=1500
self.BreakLate.LimitXmin=0
@ -1858,26 +1879,22 @@ function CARRIERTRAINER:_LSOgrade(playerData)
elseif playerData.landed then
--local gdata=playerData.groove.XX --#CARRIERTRAINER.GrooveData
grade=grade..playerData.groove.XX
grade=grade..playerData.groove.RB
grade=grade..playerData.groove.IM
grade=grade..playerData.groove.IC
grade=grade..playerData.groove.AR
grade=grade..playerData.groove.IW
grade=grade..playerData.groove.IW
else
end
end
--- Grade approach.
--- Grade flight data.
-- @param #CARRIERTRAINER self
-- @param #CARRIERTRAINER.GrooveData fdata Flight data in the groove.
-- @return #string LSO grade.
-- @return #string LSO grade or empty string if flight data table is nil.
function CARRIERTRAINER:_Flightdata2Text(fdata)
local function little(text)
@ -1887,18 +1904,19 @@ function CARRIERTRAINER:_Flightdata2Text(fdata)
return string.format("_%s_", text)
end
-- No flight data ==> return empty string.
if fdata==nil then
return ""
end
-- Flight data.
local step=fdata.Step
local AOA=fdata.AoA
local GSE=fdata.GSE
local LUE=fdata.LUE
local ROL=fdata.Roll
local Y=self:_GS(step)
-- Speed.
local S=nil
if AOA>9.3 then
S=underline("F")
@ -1914,6 +1932,7 @@ function CARRIERTRAINER:_Flightdata2Text(fdata)
S=little("SLO")
end
-- Alitude.
local A=nil
if GSE>1 then
A=underline("H")
@ -1929,6 +1948,7 @@ function CARRIERTRAINER:_Flightdata2Text(fdata)
A="LO"
end
-- Line up.
local D=nil
if LUE>3 then
D=underline("LUL")
@ -1944,6 +1964,7 @@ function CARRIERTRAINER:_Flightdata2Text(fdata)
D=little("LUL")
end
-- Compile.
local G=""
if S then
G=G..S
@ -1955,8 +1976,9 @@ function CARRIERTRAINER:_Flightdata2Text(fdata)
G=G..D
end
-- Add current step.
if G~="" then
G=G..Y
G=G..self:_GS(step)
end
return G
@ -1991,44 +2013,44 @@ end
-- @param #CARRIERTRAINER.Checkpoint checkpoint Checkpoint.
-- @param #number altitude Player's current altitude in meters.
-- @return #string Feedback text.
-- @return #string Debriefing text.
function CARRIERTRAINER:_AltitudeCheck(playerData, checkpoint, altitude)
-- Player altitude.
local altitude=playerData.unit:GetAltitude()
-- Get relative score.
local lowscore, badscore = self:_GetGoodBadScore(playerData)
local lowscore, badscore=self:_GetGoodBadScore(playerData)
-- Altitude error +-X%
local _error=(altitude-checkpoint.Altitude)/checkpoint.Altitude*100
local score
local hint
local steptext=self:_StepName(playerData.step)
if _error>badscore then
score = -10
hint = string.format("You're high %s. ", steptext)
hint=string.format("You're high. ")
elseif _error>lowscore then
score = -5
hint = string.format("You're slightly high %s. ", steptext)
hint= string.format("You're slightly high. ")
elseif _error<-badscore then
score = -10
hint = string.format("You're low %s.", steptext)
hint=string.format("You're low. ")
elseif _error<-lowscore then
score = -5
hint = string.format("You're slightly low %s. ", steptext)
hint=string.format("You're slightly low. ")
else
score = 0
hint = string.format("Good altitude %s. ", steptext)
hint=string.format("Good altitude. ")
end
hint=hint..string.format(" Altitude %d ft = %d%% deviation from %d ft target alt.", UTILS.MetersToFeet(altitude), _error, UTILS.MetersToFeet(checkpoint.Altitude))
-- Extend or decrease depending on skill.
if playerData.difficulty==CARRIERTRAINER.Difficulty.EASY then
hint=hint..string.format("Optimal altitude is %d ft.\n", UTILS.MetersToFeet(checkpoint.Altitude))
elseif playerData.difficulty==CARRIERTRAINER.Difficulty.NORMAL then
hint=hint.."\n"
elseif playerData.difficulty==CARRIERTRAINER.Difficulty.HARD then
hint=""
end
-- Set score.
playerData.score=playerData.score+score
return hint
-- Debrief text.
local debrief=string.format("Altitude %d ft = %d%% deviation from %d ft optimum.", UTILS.MetersToFeet(altitude), _error, UTILS.MetersToFeet(checkpoint.Altitude))
return hint, debrief
end
--- Evaluate player's altitude at checkpoint.
@ -2037,6 +2059,7 @@ end
-- @param #CARRIERTRAINER.Checkpoint checkpoint Checkpoint.
-- @param #number distance Player's current distance to the boat in meters.
-- @return #string Feedback message text.
-- @return #string Debriefing text.
function CARRIERTRAINER:_DistanceCheck(playerData, checkpoint, distance)
-- Get relative score.
@ -2045,32 +2068,33 @@ function CARRIERTRAINER:_DistanceCheck(playerData, checkpoint, distance)
-- Altitude error +-X%
local _error=(distance-checkpoint.Distance)/checkpoint.Distance*100
local score
local hint
local steptext=self:_StepName(playerData.step)
if _error>badscore then
score = -10
hint = string.format("You're too far from the boat!")
elseif _error>lowscore then
score = -5
hint = string.format("You're slightly too far from the boat.")
hint=string.format("You're too far from the boat! ")
elseif _error>lowscore then
hint=string.format("You're slightly too far from the boat. ")
elseif _error<-badscore then
score = -10
hint = string.format( "You're too close to the boat!")
hint=string.format( "You're too close to the boat! ")
elseif _error<-lowscore then
score = -5
hint = string.format("You're slightly too far from the boat.")
hint=string.format("You're slightly too far from the boat. ")
else
score = 0
hint = string.format("Perfect distance to the boat.")
hint=string.format("Perfect distance to the boat. ")
end
hint=hint..string.format(" Distance %.1f NM = %d%% deviation from %.1f NM optimal distance.",UTILS.MetersToNM(distance), _error, UTILS.MetersToNM(checkpoint.Distance))
-- Extend or decrease depending on skill.
if playerData.difficulty==CARRIERTRAINER.Difficulty.EASY then
hint=hint..string.format(" Optimal distance is %d NM.", UTILS.MetersToNM(checkpoint.Distance))
elseif playerData.difficulty==CARRIERTRAINER.Difficulty.NORMAL then
hint=hint.."\n"
elseif playerData.difficulty==CARRIERTRAINER.Difficulty.HARD then
hint=""
end
-- Set score.
playerData.score=playerData.score+score
return hint
-- Debriefing text.
local debrief=string.format("Distance %.1f NM = %d%% deviation from %.1f NM optimum.",UTILS.MetersToNM(distance), _error, UTILS.MetersToNM(checkpoint.Distance))
return hint, debrief
end
--- Score for correct AoA.
@ -2078,7 +2102,8 @@ end
-- @param #CARRIERTRAINER.PlayerData playerData Player data.
-- @param #CARRIERTRAINER.Checkpoint checkpoint Checkpoint.
-- @param #number aoa Player's current Angle of attack.
-- @return #string hint Feedback message text.
-- @return #string Feedback message text or easy and normal difficulty level or nil for hard.
-- @return #string Debriefing text.
function CARRIERTRAINER:_AoACheck(playerData, checkpoint, aoa)
-- Get relative score.
@ -2087,31 +2112,32 @@ function CARRIERTRAINER:_AoACheck(playerData, checkpoint, aoa)
-- Altitude error +-X%
local _error=(aoa-checkpoint.AoA)/checkpoint.AoA*100
local score = 0
local hint=""
local hint
if _error>badscore then --Slow
score = -10
hint = "You're slow."
hint="You're slow. "
elseif _error>lowscore then --Slightly slow
score = -5
hint = "You're slightly slow."
hint="You're slightly slow. "
elseif _error<-badscore then --Fast
score = -10
hint = "You're fast."
hint="You're fast. "
elseif _error<-lowscore then --Slightly fast
score = -5
hint = "You're slightly fast."
hint="You're slightly fast. "
else --On speed
score = 0
hint = "You're on speed."
hint="You're on speed. "
end
-- Extend or decrease depending on skill.
if playerData.difficulty==CARRIERTRAINER.Difficulty.EASY then
hint=hint..string.format(" Optimal AoA is %.1f.", checkpoint.AoA)
elseif playerData.difficulty==CARRIERTRAINER.Difficulty.NORMAL then
hint=hint.."\n"
elseif playerData.difficulty==CARRIERTRAINER.Difficulty.HARD then
hint=""
end
hint=hint..string.format(" AoA %.1f = %d %% deviation from %.1f target AoA.", aoa, _error, checkpoint.AoA)
-- Set score.
playerData.score=playerData.score+score
-- Debriefing text.
local debrief=string.format("AoA %.1f = %d%% deviation from %.1f optimum.", aoa, _error, checkpoint.AoA)
return hint
return hint, debrief
end
@ -2122,72 +2148,16 @@ end
-- @param #CARRIERTRAINER.PlayerData playerData Player data.
-- @param #boolean clear If true, clear screen from previous messages.
function CARRIERTRAINER:_SendMessageToPlayer(message, duration, playerData, clear)
if playerData.client then
--MESSAGE:New(string.format("%s, %s, ", self.alias, playerData.callsign)..message, duration, nil, clear):ToClient(playerData.client)
if message then
if playerData.client then
--MESSAGE:New(string.format("%s, %s, ", self.alias, playerData.callsign)..message, duration, nil, clear):ToClient(playerData.client)
end
local text=string.format("%s, %s, %s", self.alias, playerData.callsign, message)
MESSAGE:New(text, duration, nil, clear):ToAll()
env.info(text)
end
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.
-- @param #CARRIERTRAINER self
-- @param #CARRIERTRAINER.PlayerData playerData Player data.
-- @param #number duration Duration for message display.
function CARRIERTRAINER:_PrintFinalScore(playerData, duration, wire)
local wireText = ""
if(wire == -2) then
wireText = "Aborted approach"
elseif(wire == -1) then
wireText = "Wave-off"
elseif(wire == 0) then
wireText = "Bolter"
else
wireText = wire .. "-wire"
end
MessageToAll( playerData.callsign .. " - Final score: " .. playerData.score .. " / 140 (" .. wireText .. ")", duration, "FinalScore" )
--self:_SendMessageToPlayer( playerData.summary, duration, playerData )
end
--- Collect result.
-- @param #CARRIERTRAINER self
-- @param #CARRIERTRAINER.PlayerData playerData Player data.
-- @param #number wire Trapped wire.
function CARRIERTRAINER:_HandleCollectedResult(playerData, wire)
local newString = ""
if(wire == -2) then
newString = playerData.score .. " (Aborted)"
elseif(wire == -1) then
newString = playerData.score .. " (Wave-off)"
elseif(wire == 0) then
newString = playerData.score .. " (Bolter)"
else
newString = playerData.score .. " (" .. wire .."W)"
end
playerData.totalscore = playerData.totalscore + playerData.score
playerData.passes = playerData.passes + 1
--TODO: collect results
--[[
if playerData.collectedResultString == "" then
playerData.collectedResultString = newString
else
playerData.collectedResultString = playerData.collectedResultString .. ", " .. newString
MessageToAll( playerData.callsign .. "'s " .. playerData.passes .. " passes: " .. playerData.collectedResultString .. " (TOTAL: " .. playerData.totalscore .. ")" , 30, "CollectedResult" )
end
]]
local heading=playerData.unit:GetCoordinate():HeadingTo(self.registerZone:GetCoordinate())
local distance=playerData.unit:GetCoordinate():Get2DDistance(self.registerZone:GetCoordinate())
local text=string.format("%s, fly heading %d for %d NM to restart the pattern.", playerData.callsign, heading, UTILS.MetersToNM(distance))
--"Return south 4 nm (over the trailing ship), towards WP 1, to restart the pattern."
self:_SendMessageToPlayer(text, 30, playerData)
end
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
-- @param #CARRIERTRAINER self
-- @param #string _unitName Name of the player unit.
@ -2278,6 +2248,8 @@ function CARRIERTRAINER:_AddF10Commands(_unitName)
missionCommands.addCommandForGroup(_gid, "Carrier Info", _trainPath, self._DisplayCarrierInfo, self, _unitName)
missionCommands.addCommandForGroup(_gid, "Weather Report", _trainPath, self._DisplayCarrierWeather, self, _unitName)
--TODO: Flare carrier.
missionCommands.addCommandForGroup(_gid, "Attitude Monitor ON/OFF", _trainPath, self._AttitudeMonitor, self, playername)
end
else
self:T(self.lid.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName)
@ -2327,7 +2299,7 @@ function CARRIERTRAINER:_DisplayScoreBoard(_unitName)
end
--Sort list!
local _sort = function( a,b ) return a.hits > b.hits end
local _sort=function(a, b) return a.hits>b.hits end
table.sort(_playerResults,_sort)
-- Add top 10 results.
@ -2346,14 +2318,27 @@ function CARRIERTRAINER:_DisplayScoreBoard(_unitName)
end
--- Turn player's aircraft attitude display on or off.
-- @param #CARRIERTRAINER self
-- @param #string playername Player name.
function CARRIERTRAINER:_AttitudeMonitor(playername)
self:E({playername=playername})
local playerData=self.players[playername] --#CARRIERTRAINER.PlayerData
if playerData then
playerData.attitudemonitor=not playerData.attitudemonitor
end
end
--- Set difficulty level.
-- @param #CARRIERTRAINER self
-- @param #string playernaame Player name.
-- @param #string playername Player name.
-- @param #CARRIERTRAINER.Difficulty difficulty Difficulty level.
function CARRIERTRAINER:_SetDifficulty(playername, difficulty)
self:E({difficulty=difficulty, playername=playername})
local playerData=self.players[playername] --CARRIERTRAINER.PlayerData
local playerData=self.players[playername] --#CARRIERTRAINER.PlayerData
if playerData then
playerData.difficulty=difficulty
@ -2479,27 +2464,3 @@ function CARRIERTRAINER:_DisplayCarrierWeather(_unitname)
end
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- LSO Class
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- LSO class.
-- @type LSO
-- @field #string ClassName Name of the class.
-- @extends Core.Fsm#FSM
--- Landing Signal Officer
--
-- ===
--
-- ![Banner Image](..\Presentations\LSO\LSO_Main.png)
--
-- # The Landing Signal Officer
--
-- bla bla
--
-- @field #LSO
LSO = {
ClassName = "LSO",
}