diff --git a/Moose Development/Moose/Functional/CarrierTrainer.lua b/Moose Development/Moose/Functional/CarrierTrainer.lua index dbc23fad9..4e00ea75d 100644 --- a/Moose Development/Moose/Functional/CarrierTrainer.lua +++ b/Moose Development/Moose/Functional/CarrierTrainer.lua @@ -115,19 +115,20 @@ CARRIERTRAINER.Difficulty={ --- Player data table holding all important parameters for each player. -- @type CARRIERTRAINER.PlayerData -- @field #number id Player ID. --- @field #string callsign Callsign of player. --- @field #number score Player score. --- @field #number totalscore Score of all landing attempts. --- @field #number passes Number of passes. --- @field #string collectedResultString Results text of all passes. -- @field Wrapper.Unit#UNIT unit Aircraft unit of the player. --- @field #number lowestAltitude Lowest altitude. --- @field #number highestCarrierXDiff --- @field #number secondsStandingStill Time player does not move after a landing attempt. +-- @field #string callsign Callsign of player. +-- @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 #string summary Result summary text. -- @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 boltered If true, player boltered. +-- @field #boolean calledball If true, player called the ball. +-- @field #number Tlso Last time the LSO gave an advice. --- Checkpoint parameters triggering the next step in the pattern. -- @type CARRIERTRAINER.Checkpoint @@ -152,7 +153,7 @@ CARRIERTRAINER.MenuF10={} --- Carrier trainer class version. -- @field #string version -CARRIERTRAINER.version="0.1.0w" +CARRIERTRAINER.version="0.1.1w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -374,18 +375,26 @@ function CARRIERTRAINER:_InitNewPlayer(unitname) local playerData={} --#CARRIERTRAINER.PlayerData + -- Player unit, client and callsign. playerData.unit = UNIT:FindByName(unitname) playerData.client = CLIENT:FindByName(playerData.unit.UnitName, nil, true) playerData.callsign = playerData.unit:GetCallsign() - playerData.totalscore = 0 - playerData.passes = 0 - playerData.collectedResultString = "" - - playerData=self:_InitNewRound(playerData) + playerData.totalscore = 0 + + -- Number of passes done by player. + playerData.passes=0 + + playerData.results={} + + -- Set difficulty level. playerData.difficulty=CARRIERTRAINER.Difficulty.NORMAL + -- Player is in the big zone around the carrier. playerData.inbigzone=playerData.unit:IsInZone(self.giantZone) + + -- Init stuff for this round. + playerData=self:_InitNewRound(playerData) return playerData end @@ -395,22 +404,26 @@ end -- @param #CARRIERTRAINER.PlayerData playerData Player data. -- @return #CARRIERTRAINER.PlayerData Initialized player data. function CARRIERTRAINER:_InitNewRound(playerData) - playerData.step = 0 - playerData.score = 0 + playerData.step=0 + playerData.score=100 + playerData.grade={} playerData.summary = "Debriefing:\n" playerData.longDownwindDone = false - playerData.highestCarrierXDiff = -9999999 - playerData.secondsStandingStill = 0 - playerData.lowestAltitude = 999999 + playerData.boltered=false + playerData.landed=false + playerData.calledball=false + playerData.Tlso=timer.getTime() return playerData end ---- Append text to summary text. +--- Append text to debrief text. -- @param #CARRIERTRAINER self -- @param #CARRIERTRAINER.PlayerData playerData Player data. --- @param #string item Text item appeded to the summary. -function CARRIERTRAINER:_AddToSummary(playerData, item) - playerData.summary = playerData.summary .. item .. "\n" +-- @param #string step Current step in the pattern. +-- @param #string item Text item appeded to the debrief. +function CARRIERTRAINER:_AddToSummary(playerData, step, item) + --playerData.summary = playerData.summary .. item .. "\n" + table.inser(playerData.debrief, {step=step, hint=item}) end --- Append text to result text. @@ -492,7 +505,9 @@ function CARRIERTRAINER:_CheckPlayerStatus() elseif playerData.step == 8 then self:_Groove(playerData) elseif playerData.step == 9 then - self:_Trap(playerData) + self:_CallTheBall(playerData) + elseif playerData.step == 99 then + self:_Debrief(playerData) end else @@ -866,7 +881,7 @@ function CARRIERTRAINER:_NewRound(playerData) self:_InitNewRound(playerData) -- Next step: start of pattern. - playerData.step = 1 + playerData.step=1 end end @@ -881,8 +896,7 @@ function CARRIERTRAINER:_Start(playerData) self:_SendMessageToPlayer(hint, 8, playerData) -- Next step: upwind. - playerData.step = 2 - + playerData.step=2 end end @@ -909,12 +923,14 @@ function CARRIERTRAINER:_Upwind(playerData) -- Get altitude. local hint=self:_AltitudeCheck(playerData, self.Upwind, altitude) + -- Message to player self:_SendMessageToPlayer(hint, 8, playerData) + -- Debrief. self:_AddToSummary(playerData, hint) -- Next step. - playerData.step = 3 + playerData.step=3 end end @@ -952,7 +968,7 @@ function CARRIERTRAINER:_Break(playerData, part) -- Send message to player. self:_SendMessageToPlayer(hint, 10, playerData) - -- Add hint to summary. + -- Debrif self:_AddToSummary(playerData, hint) -- Nest step: late break or abeam. @@ -986,7 +1002,7 @@ function CARRIERTRAINER:_CheckForLongDownwind(playerData) local hint = "Your downwind leg is too long. Turn to final earlier next time." self:_SendMessageToPlayer(hint, 10, playerData) - -- Add to debrief. + -- Debrief. self:_AddToSummary(playerData, hint) -- Decrease score. @@ -1042,7 +1058,7 @@ function CARRIERTRAINER:_Abeam(playerData) self:_SendMessageToPlayer(hintFull, 10, playerData) -- Add to debrief. - self:_AddToSummary(playerData, hintFull) + self:_AddToSummary(playerData, "Abeam", hintFull) -- Proceed to next step. playerData.step = 6 @@ -1085,7 +1101,7 @@ function CARRIERTRAINER:_Ninety(playerData) self:_SendMessageToPlayer(hintFull, 10, playerData) -- Add to debrief. - self:_AddToSummary(playerData, hintFull) + self:_AddToSummary(playerData, "At the 90:", hintFull) -- Long downwind not an issue any more playerData.longDownwindDone = true @@ -1128,14 +1144,14 @@ function CARRIERTRAINER:_Wake(playerData) self:_SendMessageToPlayer(hintFull, 10, playerData) -- Add to debrief. - self:_AddToSummary(playerData, hintFull) + self:_AddToSummary(playerData, "At the wake:", hintFull) -- Next step: Groove. playerData.step = 8 end end ---- Groove. +--- Entering the Groove. -- @param #CARRIERTRAINER self -- @param #CARRIERTRAINER.PlayerData playerData Player data table. function CARRIERTRAINER:_Groove(playerData) @@ -1179,7 +1195,7 @@ function CARRIERTRAINER:_Groove(playerData) self:_SendMessageToPlayer(hintFull, 10, playerData) -- Add to debrief. - self:_AddToSummary(playerData, hintFull) + self:_AddToSummary(playerData, "Entering the Groove:", hintFull) -- Next step. playerData.step = 9 @@ -1187,10 +1203,10 @@ function CARRIERTRAINER:_Groove(playerData) end ---- Trap. +--- Call the ball, i.e. 3/4 NM distance between aircraft and carrier. -- @param #CARRIERTRAINER self -- @param #CARRIERTRAINER.PlayerData playerData Player data table. -function CARRIERTRAINER:_Trap(playerData) +function CARRIERTRAINER:_CallTheBall(playerData) -- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier) local diffX, diffZ, rho, phi = self:_GetDistances(playerData.unit) @@ -1208,23 +1224,21 @@ function CARRIERTRAINER:_Trap(playerData) return end - if (diffX > playerData.highestCarrierXDiff) then - playerData.highestCarrierXDiff = diffX - end - - if (alt < playerData.lowestAltitude) then - playerData.lowestAltitude = alt - end - - -- Lineup. + -- Lineup. We need to correct for the end of the carrier deck and the tilted angle of the runway. + -- TODO: make this parameter of the carrier. local lineup = math.asin(diffZ/(-(diffX-100))) local lineuperror = math.deg(lineup)-10 - -- Glideslope. + -- Glideslope. Wee need to correct for the height of the deck. The ideal glide slope is 3.5 degrees. + -- TODO: make this parameter of the carrier. local glideslope = math.atan((playerData.unit:GetAltitude()-22)/(-diffX)) - local glideslopeError = math.deg(glideslope) - 3.5 + local glideslopeError = math.deg(glideslope) - 3.5 - if diffX<100 then + if diffX>-UTILS.NMToMeters(0.75) and diffX<-100 and playerData.calledball==false then + + + -- Check if we are beween 3/4 NM and end of ship. + if diffX>-UTILS.NMToMeters(0.75) and diffX<-100 then local text="Good height." if glideslopeError>1 then @@ -1259,12 +1273,12 @@ function CARRIERTRAINER:_Trap(playerData) elseif lineuperror <3 then text=text.."Right for lineup!" elseif lineuperror <1 then - text=text.."Right for lineup.." + text=text.."Right for lineup..." else text=text.."Good on lineup." end - self:_SendMessageToPlayer(text, 8,playerData) + self:_SendMessageToPlayer(text, 8, playerData) elseif (diffX > 150) then diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 5b1616c81..9a1c9306b 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -5435,7 +5435,7 @@ function RAT:_ATCInit(airports_map) if not RAT.ATC.init then local text text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay - self:T(RAT.id..text) + BASE:T(RAT.id..text) RAT.ATC.init=true for _,ap in pairs(airports_map) do local name=ap:GetName() @@ -5458,7 +5458,7 @@ end -- @param #string name Group name of the flight. -- @param #string dest Name of the destination airport. function RAT:_ATCAddFlight(name, dest) - self:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest)) + BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest)) RAT.ATC.flight[name]={} RAT.ATC.flight[name].destination=dest RAT.ATC.flight[name].Tarrive=-1 @@ -5483,7 +5483,7 @@ end -- @param #string name Group name of the flight. -- @param #number time Time the fight first registered. function RAT:_ATCRegisterFlight(name, time) - self:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.") + BASE:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.") RAT.ATC.flight[name].Tarrive=time RAT.ATC.flight[name].holding=0 end @@ -5514,7 +5514,7 @@ function RAT:_ATCStatus() -- Aircraft is holding. local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy) - self:T(RAT.id..text) + BASE:T(RAT.id..text) elseif hold==RAT.ATC.onfinal then @@ -5522,7 +5522,7 @@ function RAT:_ATCStatus() local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60) - self:T(RAT.id..text) + BASE:T(RAT.id..text) elseif hold==RAT.ATC.unregistered then @@ -5530,7 +5530,7 @@ function RAT:_ATCStatus() --self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold)) else - self:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().") + BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().") end end @@ -5572,12 +5572,12 @@ function RAT:_ATCCheck() -- Debug message. local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) - self:T(RAT.id..text) + BASE:T(RAT.id..text) else local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) - self:T(RAT.id..text) + BASE:T(RAT.id..text) -- Clear flight for landing. RAT:_ATCClearForLanding(name, flight) @@ -5705,12 +5705,7 @@ function RAT:_ATCQueue() for k,v in ipairs(_queue) do table.insert(RAT.ATC.airport[airport].queue, v[1]) end - - --fvh - --for k,v in ipairs(RAT.ATC.airport[airport].queue) do - --print(string.format("queue #%02i flight \"%s\" holding %d seconds",k, v, RAT.ATC.flight[v].holding)) - --end - + end end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index dc3834638..bc1e840ae 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -69,6 +69,7 @@ -- @field #boolean autosave Automatically save assets to file when mission ends. -- @field #string autosavepath Path where the asset file is saved on auto save. -- @field #string autosavefilename File name of the auto asset save file. Default is auto generated from warehouse id and name. +-- @field #boolean safeparking If true, parking spots for aircraft are considered as occupied if e.g. a client aircraft is parked there. Default false. -- @extends Core.Fsm#FSM --- Have your assets at the right place at the right time - or not! @@ -1556,6 +1557,7 @@ WAREHOUSE = { autosave = false, autosavepath = nil, autosavefile = nil, + saveparking = false, } --- Item of the warehouse stock table. @@ -2364,6 +2366,24 @@ function WAREHOUSE:SetReportOff() return self end +--- Enable safe parking option, i.e. parking spots at an airbase will be considered as occupied when a client aircraft is parked there (even if the client slot is not taken by a player yet). +-- Note that also incoming aircraft can reserve/occupie parking spaces. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetSafeParkingOn() + self.safeparking=true + return self +end + +--- Disable safe parking option. Note that is the default setting. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetSafeParkingOff() + self.safeparking=false + return self +end + + --- Set interval of status updates. Note that normally only one request can be processed per time interval. -- @param #WAREHOUSE self -- @param #number timeinterval Time interval in seconds. @@ -7004,20 +7024,6 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) table.insert(obstacles,{coord=_coord, size=_size, name=_name, type="scenery"}) end - --[[ - -- TODO Clients? Unoccupied client aircraft are also important! Are they already included in scanned units maybe? - local clients=_DATABASE.CLIENTS - for _,_client in pairs(clients) do - local client=_client --Wrapper.Client#CLIENT - env.info(string.format("FF Client name %s", client:GetName())) - local unit=UNIT:FindByName(client:GetName()) - --local unit=client:GetClientGroupUnit() - local _coord=unit:GetCoordinate() - local _name=unit:GetName() - local _size=self:_GetObjectSize(client:GetClientGroupDCSUnit()) - table.insert(obstacles,{coord=_coord, size=_size, name=_name, type="client"}) - end - ]] end -- Parking data for all assets. @@ -7050,10 +7056,17 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local _toac=parkingspot.TOAC --env.info(string.format("FF asset=%s (id=%d): needs terminal type=%d, id=%d, #obstacles=%d", _asset.templatename, _asset.uid, terminaltype, _termid, #obstacles)) - - -- Loop over all obstacles. + local free=true local problem=nil + + -- Safe parking using TO_AC from DCS result. + if self.safeparking and _toac then + free=false + self:T("Parking spot %d is occupied by other aircraft taking off or landing.", _termid) + end + + -- Loop over all obstacles. for _,obstacle in pairs(obstacles) do -- Check if aircraft overlaps with any obstacle.