CT v0.1.7

This commit is contained in:
Frank 2018-11-05 00:35:48 +01:00
parent 087ac992a2
commit 1febe765bf
4 changed files with 245 additions and 127 deletions

View File

@ -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. --- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#Distance Distance The Distance to be added in meters. -- @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. -- @return #COORDINATE The new calculated COORDINATE.
function COORDINATE:Translate( Distance, Angle ) function COORDINATE:Translate( Distance, Angle )
local SX = self.x local SX = self.x
local SY = self.z 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 TX = Distance * math.cos( Radians ) + SX
local TY = Distance * math.sin( Radians ) + SY local TY = Distance * math.sin( Radians ) + SY

View File

@ -195,6 +195,49 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
end 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}. --- Respawns the original @{Static}.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @return #SPAWNSTATIC -- @return #SPAWNSTATIC

View File

@ -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 -- @module Functional.CarrierTrainer
-- @image MOOSE.JPG -- @image MOOSE.JPG
@ -151,7 +151,7 @@ CARRIERTRAINER.LSOcall={
BOLTER=USERSOUND:New("LSO - Bolter.ogg"), BOLTER=USERSOUND:New("LSO - Bolter.ogg"),
BOLTERT="Bolter, Bolter!", BOLTERT="Bolter, Bolter!",
LONGGROOVE=USERSOUND:New("LSO - Long in Groove.ogg"), 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. --- 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 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. -- @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={ CARRIERTRAINER.Difficulty={
EASY="Rookey", EASY="Flight Student",
NORMAL="Naval Aviator", NORMAL="Naval Aviator",
HARD="TOPGUN Graduate", HARD="TOPGUN Graduate",
} }
--- Groove position. --- Groove position.
-- @type CARRIERTRAINER.GroovePos -- @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 RB Roger ball.
-- @field #string IM In the middle. -- @field #string IM In the middle.
-- @field #string IC In close. -- @field #string IC In close.
-- @field #string AR At the ramp. -- @field #string AR At the ramp.
-- @field #string IW In the wires.
CARRIERTRAINER.GroovePos={ CARRIERTRAINER.GroovePos={
X="X", X0="X0",
XX="X",
RB="RB", RB="RB",
IM="IM", IM="IM",
IC="IC", IC="IC",
AR="AR", AR="AR",
IW="IW",
} }
--- Groove data. --- Groove data.
@ -187,18 +191,18 @@ CARRIERTRAINER.GroovePos={
-- @field #number Alt Altitude in meters. -- @field #number Alt Altitude in meters.
-- @field #number GSE Glide slope error in degrees. -- @field #number GSE Glide slope error in degrees.
-- @field #number LUE Lineup 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 -- @type CARRIERTRAINER.PlayerData
-- @field #number id Player ID. -- @field Wrapper.Client#CLIENT client Client object of player.
-- @field Wrapper.Unit#UNIT unit Aircraft unit of the player. -- @field Wrapper.Unit#UNIT unit Aircraft of the player.
-- @field #string callsign Callsign of player. -- @field #string callsign Callsign of player.
-- @field #string difficulty Difficulty level.
-- @field #number score Player score of the current pass. -- @field #number score Player score of the current pass.
-- @field #number passes Number of passes. -- @field #number passes Number of passes.
-- @field #table debrief Debrief analysis of the current step of this pass. -- @field #table debrief Debrief analysis of the current step of this pass.
-- @field #table results Results of all passes. -- @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 inbigzone If true, player is in the big zone.
-- @field #boolean landed If true, player landed or attempted to land. -- @field #boolean landed If true, player landed or attempted to land.
-- @field #boolean bolter If true, LSO told player to bolter. -- @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 waveoff If true, player was waved off during final approach.
-- @field #boolean patternwo If true, playe was waved of during the pattern. -- @field #boolean patternwo If true, playe was waved of during the pattern.
-- @field #number Tlso Last time the LSO gave an advice. -- @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. --- Checkpoint parameters triggering the next step in the pattern.
-- @type CARRIERTRAINER.Checkpoint -- @type CARRIERTRAINER.Checkpoint
@ -231,7 +235,7 @@ CARRIERTRAINER.MenuF10={}
--- Carrier trainer class version. --- Carrier trainer class version.
-- @field #string version -- @field #string version
CARRIERTRAINER.version="0.1.6" CARRIERTRAINER.version="0.1.7"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -257,7 +261,7 @@ CARRIERTRAINER.version="0.1.6"
-- @param #CARRIERTRAINER self -- @param #CARRIERTRAINER self
-- @param carriername Name of the aircraft carrier unit as defined in the mission editor. -- @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. -- @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) function CARRIERTRAINER:New(carriername, alias)
-- Inherit everthing from FSM class. -- Inherit everthing from FSM class.
@ -267,13 +271,15 @@ function CARRIERTRAINER:New(carriername, alias)
self.carrier=UNIT:FindByName(carriername) self.carrier=UNIT:FindByName(carriername)
if self.carrier then if self.carrier then
-- Carrier zones.
self.registerZone = ZONE_UNIT:New("registerZone", self.carrier, 2500, {dx = -5000, dy = 100, relative_to_unit=true}) 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.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}) self.giantZone = ZONE_UNIT:New("giantZone", self.carrier, 30000, {dx = 0, dy = 0, relative_to_unit=true})
else 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) 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() MESSAGE:New(text, 120):ToAll()
self:E(self.lid..text) self:E(text)
return nil return nil
end end
@ -411,42 +417,62 @@ function CARRIERTRAINER:_CheckPlayerStatus()
-- Check if player was previously not inside the zone. -- Check if player was previously not inside the zone.
if playerData.inbigzone==false then 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) 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 heading=playerData.unit:GetCoordinate():HeadingTo(self.registerZone:GetCoordinate())
local distance=playerData.unit:GetCoordinate():Get2DDistance(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) text=text..string.format("Fly heading %d for %.1f NM and turn to BRC.", heading, distance)
MESSAGE:New(text, 5):ToClient(playerData.client) MESSAGE:New(text, 5):ToClient(playerData.client)
end end
if playerData.step==0 and unit:InAir() then if playerData.step==0 and unit:InAir() then
-- New approach.
self:_NewRound(playerData) self:_NewRound(playerData)
-- Jump to Groove for testing. -- Jump to Groove for testing.
if self.groovedebug then
playerData.step=8 playerData.step=8
self.groovedebug=false
end
elseif playerData.step == 1 then elseif playerData.step == 1 then
-- Entering the pattern.
self:_Start(playerData) self:_Start(playerData)
elseif playerData.step == 2 then elseif playerData.step == 2 then
-- Upwind leg.
self:_Upwind(playerData) self:_Upwind(playerData)
elseif playerData.step == 3 then elseif playerData.step == 3 then
-- Early break.
self:_Break(playerData, "early") self:_Break(playerData, "early")
elseif playerData.step == 4 then elseif playerData.step == 4 then
-- Late break.
self:_Break(playerData, "late") self:_Break(playerData, "late")
elseif playerData.step == 5 then elseif playerData.step == 5 then
-- Abeam position.
self:_Abeam(playerData) self:_Abeam(playerData)
elseif playerData.step == 6 then elseif playerData.step == 6 then
-- Check long down wind leg. -- Check long down wind leg.
if playerData.longDownwindDone==false then
self:_CheckForLongDownwind(playerData) self:_CheckForLongDownwind(playerData)
end -- At the ninety.
self:_Ninety(playerData) self:_Ninety(playerData)
elseif playerData.step==7 then elseif playerData.step==7 then
-- In the wake.
self:_Wake(playerData) self:_Wake(playerData)
elseif playerData.step==8 then elseif playerData.step==8 then
-- Entering the groove.
self:_Groove(playerData) self:_Groove(playerData)
elseif playerData.step>=90 and playerData.step<=99 then elseif playerData.step>=90 and playerData.step<=99 then
-- In the groove.
self:_CallTheBall(playerData) self:_CallTheBall(playerData)
elseif playerData.step==999 then 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 end
else else
@ -509,10 +535,8 @@ function CARRIERTRAINER:OnEventBirth(EventData)
-- Init player data. -- Init player data.
self.players[_playername]=self:_InitPlayer(_unitName) self.players[_playername]=self:_InitPlayer(_unitName)
-- Test -- Start in the groove for debugging.
--CARRIERTRAINER.LSOcall.HIGHL:ToGroup(_group) self.groovedebug=false
--CARRIERTRAINER.LSOcall.CALLTHEBALL:ToGroup(_group, 10)
--MESSAGE:New(CARRIERTRAINER.LSOcall.HIGHT, 5):ToAllIf(self.Debug)
end end
end end
@ -547,19 +571,27 @@ function CARRIERTRAINER:OnEventLand(EventData)
-- Coordinate at landing event -- Coordinate at landing event
local coord=playerData.unit:GetCoordinate() 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. -- We did land.
playerData.landed=true playerData.landed=true
--TODO: maybe check that we actually landed on the right carrier. --TODO: maybe check that we actually landed on the right carrier.
-- Call trapped function in 5 seconds to make sure we did not bolter. -- Call trapped function in 3 seconds to make sure we did not bolter.
SCHEDULER:New(nil, self._Trapped,{self, playerData, coord}, 5) SCHEDULER:New(nil, self._Trapped,{self, playerData, coord}, 3)
end end
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- CARRIER TRAINING functions -- CARRIER TRAINING functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -570,6 +602,7 @@ end
-- @return #CARRIERTRAINER.PlayerData Player data. -- @return #CARRIERTRAINER.PlayerData Player data.
function CARRIERTRAINER:_InitPlayer(unitname) function CARRIERTRAINER:_InitPlayer(unitname)
-- Player data.
local playerData={} --#CARRIERTRAINER.PlayerData local playerData={} --#CARRIERTRAINER.PlayerData
-- Player unit, client and callsign. -- Player unit, client and callsign.
@ -602,15 +635,16 @@ end
-- @param #CARRIERTRAINER.PlayerData playerData Player data. -- @param #CARRIERTRAINER.PlayerData playerData Player data.
-- @return #CARRIERTRAINER.PlayerData Initialized player data. -- @return #CARRIERTRAINER.PlayerData Initialized player data.
function CARRIERTRAINER:_InitNewRound(playerData) function CARRIERTRAINER:_InitNewRound(playerData)
self:I(self.lid..string.format("New round for player %s.", playerData.callsign))
playerData.step=0 playerData.step=0
playerData.score=100 playerData.score=100
playerData.grade={} playerData.groove={}
playerData.debrief={} playerData.debrief={}
playerData.longDownwindDone=false playerData.patternwo=false
playerData.waveoff=false
playerData.bolter=false
playerData.boltered=false playerData.boltered=false
playerData.landed=false playerData.landed=false
playerData.waveoff=false
playerData.patternwo=false
playerData.Tlso=timer.getTime() playerData.Tlso=timer.getTime()
return playerData return playerData
end end
@ -642,7 +676,7 @@ function CARRIERTRAINER:_Start(playerData)
-- Inform player. -- Inform player.
local hint = string.format("Entering the pattern.") local hint = string.format("Entering the pattern.")
if playerData.difficulty==CARRIERTRAINER.Difficulty.EASY then 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 end
-- Send message. -- Send message.
@ -750,7 +784,7 @@ function CARRIERTRAINER:_CheckForLongDownwind(playerData)
local relhead=self:_GetRelativeHeading(playerData.unit) local relhead=self:_GetRelativeHeading(playerData.unit)
-- One NM from carrier is too far. -- 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) local text=string.format("Long groove check: X=%d, relhead=%.1f", X, relhead)
self:T(text) self:T(text)
@ -773,9 +807,6 @@ function CARRIERTRAINER:_CheckForLongDownwind(playerData)
local grade="LIG PATTERN WAVE OFF - CUT 1 PT" local grade="LIG PATTERN WAVE OFF - CUT 1 PT"
-- Long downwind done!
playerData.longDownwindDone = true
-- Next step: Debriefing. -- Next step: Debriefing.
playerData.step=999 playerData.step=999
end end
@ -835,7 +866,7 @@ function CARRIERTRAINER:_Ninety(playerData)
-- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier) -- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier)
local X, Z = self:_GetDistances(playerData.unit) 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 if self:_CheckAbort(X, Z, self.Ninety) then
self:_AbortPattern(playerData, X, Z, self.Ninety) self:_AbortPattern(playerData, X, Z, self.Ninety)
return return
@ -866,9 +897,6 @@ function CARRIERTRAINER:_Ninety(playerData)
-- Add to debrief. -- Add to debrief.
self:_AddToSummary(playerData, "At the 90", hintFull) self:_AddToSummary(playerData, "At the 90", hintFull)
-- Long downwind not an issue any more
playerData.longDownwindDone=true
-- Next step: wake. -- Next step: wake.
playerData.step = 7 playerData.step = 7
@ -933,19 +961,21 @@ function CARRIERTRAINER:_Groove(playerData)
return return
end end
-- Call the ball distance.
local calltheball=UTILS.NMToMeters(0.75)+math.abs(self.sterndist)
local calltheball=UTILS.NMToMeters(0.75) local relhead=self:_GetRelativeHeading(playerData.unit)+self.rwyangle
local lineup=self:_Lineup(playerData)-self.rwyangle
local roll=playerData.unit:GetRoll()
if rho<=calltheball then env.info(string.format("FF relhead=%d lineup=%d roll=%d", relhead, lineup, roll))
if math.abs(lineup)<5 and math.abs(relhead)<10 then
-- Get player altitude and AoA. -- Get player altitude and AoA.
local alt = playerData.unit:GetAltitude() local alt = playerData.unit:GetAltitude()
local aoa = playerData.unit:GetAoA() local aoa = playerData.unit:GetAoA()
self:_SendMessageToPlayer("Call the ball.", 8, playerData)
CARRIERTRAINER.LSOcall.CALLTHEBALL:ToGroup(playerData.unit:GetGroup())
-- Grade altitude. -- Grade altitude.
local hintAlt=self:_AltitudeCheck(playerData, self.Groove, alt) local hintAlt=self:_AltitudeCheck(playerData, self.Groove, alt)
@ -959,22 +989,20 @@ function CARRIERTRAINER:_Groove(playerData)
self:_SendMessageToPlayer(hintFull, 10, playerData) self:_SendMessageToPlayer(hintFull, 10, playerData)
-- Add to debrief. -- Add to debrief.
self:_AddToSummary(playerData, "Calling the ball", hintFull) self:_AddToSummary(playerData, "Enter Groove", hintFull)
-- Gather pilot data.
local groovedata={} --#CARRIERTRAINER.GrooveData local groovedata={} --#CARRIERTRAINER.GrooveData
groovedata.Alt=alt groovedata.Alt=alt
groovedata.AoA=aoa groovedata.AoA=aoa
groovedata.GSE=self:_Glideslope(playerData)-3.5 groovedata.GSE=self:_Glideslope(playerData)-3.5
groovedata.LUE=self:_Lineup(playerData)-10 groovedata.LUE=self:_Lineup(playerData)-self.rwyangle
groovedata.Step=playerData.step groovedata.Step=playerData.step
-- Init groove table. -- Groove
playerData.Groove={} playerData.groove.X0=groovedata
-- Next step: X call the ball.
playerData.Groove.X=groovedata
-- Next step: roger ball.
playerData.step=90 playerData.step=90
end end
@ -1013,51 +1041,63 @@ function CARRIERTRAINER:_CallTheBall(playerData)
local AoA=playerData.unit:GetAoA() local AoA=playerData.unit:GetAoA()
-- Ranges in the groove. -- Ranges in the groove.
local RRB=UTILS.NMToMeters(0.500) -- Roger Ball! call. local RXX=UTILS.NMToMeters(0.750)+math.abs(self.sterndist) -- Start of groove. 0.75 = 1389 m
local RIM=UTILS.NMToMeters(0.375) -- In the Middle 0.75/2. local RRB=UTILS.NMToMeters(0.500)+math.abs(self.sterndist) -- Roger Ball! call. 0.5 = 926 m
local RIC=UTILS.NMToMeters(0.100) -- In Close. local RIM=UTILS.NMToMeters(0.375)+math.abs(self.sterndist) -- In the Middle 0.75/2. 0.375 = 695 m
local RAR=UTILS.NMToMeters(0.050) -- At the Ramp. 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 -- Data
local groovedata={} --#CARRIERTRAINER.GrooveData local groovedata={} --#CARRIERTRAINER.GrooveData
groovedata.Step=playerData.step
groovedata.Alt=alt groovedata.Alt=alt
groovedata.AoA=AoA groovedata.AoA=AoA
groovedata.GSE=glideslopeError groovedata.GSE=glideslopeError
groovedata.LUE=lineupError groovedata.LUE=lineupError
groovedata.Step=playerData.step
if rho<=RRB and playerData.step==90 then if rho<=RXX and playerData.step==90 then
-- Roger ball! -- LSO "Call the ball" call.
self:_SendMessageToPlayer(CARRIERTRAINER.LSOcall.ROGERBALLT, 8, playerData) self:_SendMessageToPlayer("Call the ball.", 8, playerData)
CARRIERTRAINER.LSOcall.ROGERBALL:ToGroup(player) CARRIERTRAINER.LSOcall.CALLTHEBALL:ToGroup(playerData.unit:GetGroup())
playerData.Tlso=timer.getTime()
-- Store data. -- Next step: roger ball.
playerData.Groove.RB=groovedata
-- Next step: in the middle.
playerData.step=91 playerData.step=91
elseif rho<=RIM and playerData.step==91 then elseif rho<=RRB and playerData.step==91 then
-- 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
-- Next step: in the middle.
playerData.step=92
elseif rho<=RIM and playerData.step==92 then
--TODO: grade for IM --TODO: grade for IM
self:_SendMessageToPlayer("IM", 8, playerData) self:_SendMessageToPlayer("IM", 8, playerData)
env.info(string.format("FF IM=%d", rho)) env.info(string.format("FF IM=%d", rho))
-- Store data. -- Store data.
playerData.Groove.IM=groovedata playerData.groove.IM=groovedata
-- Next step: in close. -- 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? --TODO: grade for IC, call wave off?
self:_SendMessageToPlayer("IC", 8, playerData) self:_SendMessageToPlayer("IC", 8, playerData)
env.info(string.format("FF IC=%d", rho)) env.info(string.format("FF IC=%d", rho))
-- Store data. -- Store data.
playerData.Groove.IC=groovedata playerData.groove.IC=groovedata
-- Check if player should wave off. -- Check if player should wave off.
local waveoff=self:_CheckWaveOff(glideslopeError, lineupError, AoA) local waveoff=self:_CheckWaveOff(glideslopeError, lineupError, AoA)
@ -1068,26 +1108,28 @@ function CARRIERTRAINER:_CallTheBall(playerData)
-- Wave off player. -- Wave off player.
self:_SendMessageToPlayer(CARRIERTRAINER.LSOcall.WAVEOFFT, 10, playerData) self:_SendMessageToPlayer(CARRIERTRAINER.LSOcall.WAVEOFFT, 10, playerData)
CARRIERTRAINER.LSOcall.WAVEOFF:ToGroup(playerData.unit:GetGroup()) CARRIERTRAINER.LSOcall.WAVEOFF:ToGroup(playerData.unit:GetGroup())
playerData.Tlso=timer.getTime()
-- Next step: debrief. -- Next step: debrief.
playerData.step=999 playerData.step=999
return
else else
-- Next step: at the ramp. -- Next step: at the ramp.
playerData.step=93 playerData.step=94
end end
elseif rho<=RAR and playerData.step==94 then
elseif rho<=RAR and playerData.step==93 then
--TODO: grade for AR --TODO: grade for AR
self:_SendMessageToPlayer("AR", 8, playerData) self:_SendMessageToPlayer("AR", 8, playerData)
env.info(string.format("FF AR=%d", rho)) env.info(string.format("FF AR=%d", rho))
-- Store data. -- Store data.
playerData.Groove.AR=groovedata playerData.groove.AR=groovedata
-- Next step: at the ramp. -- Next step: at the ramp.
playerData.step=94 playerData.step=95
end end
-- Time since last LSO call. -- Time since last LSO call.
@ -1095,30 +1137,34 @@ function CARRIERTRAINER:_CallTheBall(playerData)
local deltaT=time-playerData.Tlso local deltaT=time-playerData.Tlso
-- Check if we are beween 3/4 NM and end of ship. -- 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) self:_LSOcall(playerData, glideslopeError, lineupError)
elseif X>0 then elseif X>0 then
local wire = 0
local hint = ""
local score = 0
if playerData.landed then if playerData.landed then
hint = "You boltered."
else local hint="You boltered."
hint = "You were waved off."
wire = -1
score = -10
end
-- Send message to player. -- Send message to player.
self:_SendMessageToPlayer(hint, 8, playerData) self:_SendMessageToPlayer(hint, 8, playerData)
-- Add to debrief. -- Add to debrief.
self:_AddToSummary(playerData, "Bolter or wave off", hint) self:_AddToSummary(playerData, "Bolter", hint)
else
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
-- Next step: debrief. -- Next step: debrief.
playerData.step=999 playerData.step=999
@ -1135,14 +1181,17 @@ function CARRIERTRAINER:_CheckWaveOff(glideslopeError, lineupError, AoA)
local waveoff=false local waveoff=false
-- Too high or too low?
if math.abs(glideslopeError)>3 then if math.abs(glideslopeError)>3 then
waveoff=true waveoff=true
end end
-- Too far from centerline?
if math.abs(lineupError)>3 then if math.abs(lineupError)>3 then
waveoff=true waveoff=true
end end
-- Too slow or too fast?
if AoA<6.9 or AoA>9.3 then if AoA<6.9 or AoA>9.3 then
waveoff=true waveoff=true
end end
@ -1188,25 +1237,26 @@ function CARRIERTRAINER:_Trapped(playerData, pos)
local wire = 1 local wire = 1
local score = -10 local score = -10
-- Which wire -- Little offset for the exact wire positions.
if X<-14 then local wdx=11
wire = 1
score = -15 -- Which wire was caught?
elseif X<-3 then if X<-104+wdx then
wire = 2 wire=1
score = 10 elseif X<-92+wdx then
elseif X<10 then wire=2
wire = 3 elseif X<-80+wdx then
score = 20 wire=3
elseif X<68+wdx then
wire=4
else else
wire = 4 wire=0
score = 7
end end
local text=string.format("TRAPPED! %d-wire.", wire) local text=string.format("TRAPPED! %d-wire.", wire)
self:_SendMessageToPlayer(text, 30, playerData) 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) MESSAGE:New(text,30):ToAllIf(self.Debug)
env.info(text2) env.info(text2)
@ -1217,6 +1267,9 @@ function CARRIERTRAINER:_Trapped(playerData, pos)
--Boltered! --Boltered!
playerData.boltered=true playerData.boltered=true
end end
-- Next step: debriefing.
playerData.step=999
end end
--- Entering the Groove. --- Entering the Groove.
@ -1371,7 +1424,7 @@ function CARRIERTRAINER:_Debrief(playerData)
-- Debriefing text. -- Debriefing text.
local text=string.format("Debriefing:\n") local text=string.format("Debriefing:\n")
text=text..string.format("===========\n\n") text=text..string.format("===================================================\n")
for _,_data in pairs(playerData.debrief) do for _,_data in pairs(playerData.debrief) do
local step=_data.step local step=_data.step
local comment=_data.hint local comment=_data.hint
@ -1618,6 +1671,19 @@ end
-- @param #CARRIERTRAINER self -- @param #CARRIERTRAINER self
function CARRIERTRAINER:_InitStennis() 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 -- Upwind leg
self.Upwind.name="Upwind" self.Upwind.name="Upwind"
@ -1776,7 +1842,7 @@ function CARRIERTRAINER:_LSOgrade(playerData)
elseif playerData.landed then elseif playerData.landed then
local gdata=playerData.Groove.X --#CARRIERTRAINER.GrooveData local gdata=playerData.groove.X --#CARRIERTRAINER.GrooveData
else else
@ -1947,7 +2013,9 @@ function CARRIERTRAINER:_SendMessageToPlayer(message, duration, playerData, clea
if playerData.client then if playerData.client then
--MESSAGE:New(string.format("%s, %s, ", self.alias, playerData.callsign)..message, duration, nil, clear):ToClient(playerData.client) --MESSAGE:New(string.format("%s, %s, ", self.alias, playerData.callsign)..message, duration, nil, clear):ToClient(playerData.client)
end 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 end
--- Display final score. --- Display final score.

View File

@ -276,7 +276,7 @@ RANGE.id="RANGE | "
--- Range script version. --- Range script version.
-- @field #string version -- @field #string version
RANGE.version="1.2.2" RANGE.version="1.2.3"
--TODO list: --TODO list:
--TODO: Add custom weapons, which can be specified by the user. --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 self.BombtrackThreshold=distance*1000 or 25*1000
end 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 #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) function RANGE:SetRangeLocation(coordinate)
self.location=coordinate self.location=coordinate
end end
@ -471,7 +472,7 @@ end
-- If a zone is not explicitly specified, the range zone is determined by its location and radius. -- If a zone is not explicitly specified, the range zone is determined by its location and radius.
-- @param #RANGE self -- @param #RANGE self
-- @param Core.Zone#ZONE zone MOOSE zone defining the range perimeters. -- @param Core.Zone#ZONE zone MOOSE zone defining the range perimeters.
function RANGE:SetRangeLocation(zone) function RANGE:SetRangeZone(zone)
self.rangezone=zone self.rangezone=zone
end end
@ -1163,13 +1164,19 @@ function RANGE:OnEventShot(EventData)
-- Coordinate of impact point. -- 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. -- Distance from range. We dont want to smoke targets outside of the range.
local impactdist=impactcoord:Get2DDistance(self.location) 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. -- 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 if self.PlayerSettings[_playername].delaysmoke then
timer.scheduleFunction(self._DelayedSmoke, {coord=impactcoord, color=self.PlayerSettings[_playername].smokecolor}, timer.getTime() + self.TdelaySmoke) timer.scheduleFunction(self._DelayedSmoke, {coord=impactcoord, color=self.PlayerSettings[_playername].smokecolor}, timer.getTime() + self.TdelaySmoke)
else else
@ -1207,7 +1214,7 @@ function RANGE:OnEventShot(EventData)
end end
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 if _distance <= self.scorebombdistance then
-- Init bomb player results. -- Init bomb player results.
@ -1226,7 +1233,7 @@ function RANGE:OnEventShot(EventData)
-- Send message. -- Send message.
self:_DisplayMessageToGroup(_unit, _message, nil, true) self:_DisplayMessageToGroup(_unit, _message, nil, true)
elseif _distance <= self.rangeradius then elseif insidezone then
-- Send message -- Send message
local _message=string.format("%s, weapon fell more than %.1f km away from nearest range target. No score!", _callsign, self.scorebombdistance/1000) 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) self:_DisplayMessageToGroup(_unit, _message, nil, false)