From 4434d1da2122e9a57bc8be4ea986dcd1fb7c8fa9 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 24 Oct 2018 21:32:07 +0200 Subject: [PATCH] Fixes AI_FORMATION: * added stop possibility ad interval input WAREHOUSE * added autodefence assignment UNIT: * improved InAir() check function --- Moose Development/Moose/AI/AI_Formation.lua | 33 ++++- .../Moose/Functional/CarrierTrainer.lua | 123 ++++++++++++++---- .../Moose/Functional/Warehouse.lua | 2 +- .../Moose/Wrapper/Controllable.lua | 3 - Moose Development/Moose/Wrapper/Group.lua | 2 +- Moose Development/Moose/Wrapper/Unit.lua | 27 ++-- 6 files changed, 143 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index 489e69cf3..cab3c528a 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -36,6 +36,7 @@ -- @field #boolean ReportTargets If true, nearby targets are reported. -- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup. -- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup. +-- @field #number dtFollow Time step between position updates. --- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader. @@ -106,6 +107,7 @@ AI_FORMATION = { FollowScheduler = nil, OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE, OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION, + dtFollow = 0.5, } --- AI_FORMATION.Mode class @@ -139,7 +141,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin self:AddTransition( "*", "Stop", "Stopped" ) - self:AddTransition( "None", "Start", "Following" ) + self:AddTransition( {"None", "Stopped"}, "Start", "Following" ) self:AddTransition( "*", "FormationLine", "*" ) --- FormationLine Handler OnBefore for AI_FORMATION @@ -620,6 +622,16 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin return self end + +--- Set time interval between updates of the formation. +-- @param #AI_FORMATION self +-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds. +-- @return #AI_FORMATION +function AI_FORMATION:SetFollowTimeInterval(dt) --R2.1 + self.dtFollow=dt or 0.5 + return self +end + --- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. -- This allows to visualize where the escort is flying to. -- @param #AI_FORMATION self @@ -893,7 +905,20 @@ function AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1 end ---- @param Follow#AI_FORMATION self +--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected. +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups. +-- @param #string From From state. +-- @param #string Event Event. +-- @pram #string To The to state. +function AI_FORMATION:onbeforeFollowing( FollowGroupSet, From, Event, To ) --R2.1 + if From=="Stopped" then + return false -- Deny transition. + end + return true +end + +--- @param #AI_FORMATION self function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 self:F( ) @@ -1032,8 +1057,8 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 end, self, ClientUnit, CT1, CV1, CT2, CV2 ) - - self:__Follow( -0.5 ) + + self:__Follow( -self.dtFollow ) end end diff --git a/Moose Development/Moose/Functional/CarrierTrainer.lua b/Moose Development/Moose/Functional/CarrierTrainer.lua index fd3546e7c..c0dcad809 100644 --- a/Moose Development/Moose/Functional/CarrierTrainer.lua +++ b/Moose Development/Moose/Functional/CarrierTrainer.lua @@ -26,6 +26,7 @@ -- @field #boolean Debug Debug mode. Messages to all about status. -- @field Wrapper.Unit#UNIT carrier Aircraft carrier unit on which we want to practice. -- @field #string carriertype Type name of aircraft carrier. +-- @field #string alias Alias of the carrier trainer. -- @field Core.Zone#ZONE_UNIT startZone Zone in which the pattern approach starts. -- @field Core.Zone#ZONE_UNIT giantZone Large zone around the carrier to welcome players. -- @field Core.Zone#ZONE_UNIT registerZone Zone behind the carrier to register for a new approach. @@ -39,6 +40,7 @@ -- @field #CARRIERTRAINER.Checkpoint Wake Right behind the carrier. -- @field #CARRIERTRAINER.Checkpoint Groove In the groove checkpoint. -- @field #CARRIERTRAINER.Checkpoint Trap Landing checkpoint. +-- @field -- @extends Core.Fsm#FSM --- Practice Carrier Landings @@ -73,6 +75,8 @@ CARRIERTRAINER = { Wake = {}, Groove = {}, Trap = {}, + TACAN = nil, + ICLS = nil, } --- Aircraft types. @@ -148,7 +152,7 @@ CARRIERTRAINER.MenuF10={} --- Carrier trainer class version. -- @field #string version -CARRIERTRAINER.version="0.1.0" +CARRIERTRAINER.version="0.1.0w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -353,8 +357,11 @@ function CARRIERTRAINER:OnEventLand(EventData) -- Check if we caught a wire after one second. -- TODO: test this! - local playerData=self.players[_playername] - SCHEDULER:New(nil, self._Trapped,{self, playerData}, 1) + local playerData=self.players[_playername] --#CARRIERTRAINER.PlayerData + local coord=playerData.unit:GetCoordinate() + + -- Call trapped function in 5 seconds to make sure we did not bolter. + SCHEDULER:New(nil, self._Trapped,{self, playerData, coord}, 5) end end @@ -1287,18 +1294,19 @@ end --- Trapped? -- @param #CARRIERTRAINER self -- @param #CARRIERTRAINER.PlayerData playerData Player data table. -function CARRIERTRAINER:_Trapped(playerData) +-- @param Core.Point#COORDINATE pos Position of aircraft on landing event. +function CARRIERTRAINER:_Trapped(playerData, pos) -- 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) + local diffX, diffZ, rho, phi = self:_GetDistances(pos) -- Get velocities. local playerVelocity = playerData.unit:GetVelocityKMH() - local carrierVelocity = self.carrier:GetVelocityKMH() + local carrierVelocity = self.carrier:GetVelocityKMH() - if math.abs(playerVelocity-carrierVelocity) < 0.01 then - env.info("Trap identified! diff " .. diffX .. ", highestCarrierXDiff" .. playerData.highestCarrierXDiff .. ", secondsStandingStill: " .. playerData.secondsStandingStill) - + if playerData.unit:InAir()==false then + -- Seems we have successfully landed. + local wire = 1 local score = -10 @@ -1317,13 +1325,19 @@ function CARRIERTRAINER:_Trapped(playerData) score = 7 end - self:_SendMessageToPlayer( "TRAPPED! " .. wire .. "-wire!", 30, playerData ) - self:_PrintScore(score, playerData, false) + local text=string.format("TRAPPED! %d-wire.", wire) + self:_SendMessageToPlayer(text, 30, playerData) - env.info("Distance! " .. diffX .. " meters resulted in a " .. wire .. "-wire estimation."); + local text2=string.format("Distance %.1f meters resulted in a %d-wire estimate.", diffX, wire) + MESSAGE:New(text,30):ToAllIf(self.Debug) + env.info(text2) - local fullHint = "Trapped catching the " .. wire .. "-wire." + local fullHint = string.format("Trapped catching the %d-wire.", wire) + self:_AddToSummary(playerData, fullHint) + + else + --Boltered! end end @@ -1358,24 +1372,35 @@ function CARRIERTRAINER:_AddF10Commands(_unitName) if CARRIERTRAINER.MenuF10[_gid] == nil then CARRIERTRAINER.MenuF10[_gid]=missionCommands.addSubMenuForGroup(_gid, "Carrier Trainer") end - local _rangePath = missionCommands.addSubMenuForGroup(_gid, self.alias, CARRIERTRAINER.MenuF10[_gid]) - local _statsPath = missionCommands.addSubMenuForGroup(_gid, "Results", _rangePath) - local _settingsPath = missionCommands.addSubMenuForGroup(_gid, "My Settings", _rangePath) - local _infoPath = missionCommands.addSubMenuForGroup(_gid, "Carrier Info", _rangePath) + + local playerData=self.players[playername] + + -- F10/Carrier Trainer/ + local _trainPath = missionCommands.addSubMenuForGroup(_gid, self.alias, CARRIERTRAINER.MenuF10[_gid]) + -- F10/Carrier Trainer//Results + --local _statsPath = missionCommands.addSubMenuForGroup(_gid, "Results", _trainPath) + -- F10/Carrier Trainer//My Settings + local _settingsPath = missionCommands.addSubMenuForGroup(_gid, "My Settings", _trainPath) + -- F10/Carrier Trainer//My Settings/Difficulty + local _difficulPath = missionCommands.addSubMenuForGroup(_gid, "Difficulty", _settingsPath) + -- F10/Carrier Trainer//Carrier Info + local _infoPath = missionCommands.addSubMenuForGroup(_gid, "Carrier Info", _trainPath) - -- F10/On the Range//Stats/ + -- F10/Carrier Trainer//Stats/ --missionCommands.addCommandForGroup(_gid, "All Results", _statsPath, self._DisplayStrafePitResults, self, _unitName) --missionCommands.addCommandForGroup(_gid, "My Results", _statsPath, self._DisplayBombingResults, self, _unitName) --missionCommands.addCommandForGroup(_gid, "Reset All Results", _statsPath, self._ResetRangeStats, self, _unitName) - -- F10/On the Range//My Settings/ + -- F10/Carrier Trainer//My Settings/ --missionCommands.addCommandForGroup(_gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName) --missionCommands.addCommandForGroup(_gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName) - --missionCommands.addCommandForGroup(_gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName) - -- F10/On the Range//Range Information - --missionCommands.addCommandForGroup(_gid, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName) + --missionCommands.addCommandForGroup(_gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName) + -- F10/Carrier Trainer//My Settings/Difficulty + missionCommands.addCommandForGroup(_gid, "Flight Student", _difficulPath, self.SetDifficulty, self, playerData, CARRIERTRAINER.Difficulty.EASY) + missionCommands.addCommandForGroup(_gid, "Naval Aviator", _difficulPath, self.SetDifficulty, self, playerData, CARRIERTRAINER.Difficulty.NORMAL) + missionCommands.addCommandForGroup(_gid, "TOPGUN Graduate", _difficulPath, self.SetDifficulty, self, playerData, CARRIERTRAINER.Difficulty.HARD) + -- F10/Carrier Trainer//Carrier Info/ + missionCommands.addCommandForGroup(_gid, "Carrier Info", _infoPath, self._DisplayCarrierInfo, self, _unitName) missionCommands.addCommandForGroup(_gid, "Weather Report", _infoPath, self._DisplayCarrierWeather, self, _unitName) - --missionCommands.addCommandForGroup(_gid, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName) - --missionCommands.addCommandForGroup(_gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName) end else self:T(self.lid.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName) @@ -1386,11 +1411,55 @@ function CARRIERTRAINER:_AddF10Commands(_unitName) end ---- Report weather conditions at range. Temperature, QFE pressure and wind data. +--- Report information about carrier. +-- @param #CARRIERTRAINER self +-- @param #string _unitname Name of the player unit. +function CARRIERTRAINER:_DisplayCarrierInfo(_unitname) + self:F(_unitname) + + -- Get player unit and player name. + local unit, playername = self:_GetPlayerUnitAndName(_unitname) + + -- Check if we have a player. + if unit and playername then + + -- Message text. + local text=string.format("%s info:\n", self.alias) + + -- Current coordinates. + local coord=self.carrier:GetCoordinate() + + local playerData=self.players[playername] --#CARRIERTRAINER.PlayerData + + local carrierheading=self.carrier:GetHeading() + local carrierspeed=UTILS.MpsToKnots(self.carrier:GetVelocity()) + + text=text..string.format("BRC %d\n", carrierheading) + text=text..string.format("Speed %d kts\n", carrierspeed) + + + local tacan="unknown" + local icls="unknown" + if self.TACAN~=nil then + tacan=tostring(self.TACAN) + end + if self.ICLS~=nil then + icls=tostring(self.ICLS) + end + + text=text..string.format("TACAN Channel %s", tacan) + text=text..string.format("ICLS Channel %s", icls) + + end + +end + + +--- Report weather conditions at the carrier location. Temperature, QFE pressure and wind data. -- @param #CARRIERTRAINER self -- @param #string _unitname Name of the player unit. function CARRIERTRAINER:_DisplayCarrierWeather(_unitname) - self:E(_unitname) + self:F(_unitname) -- Get player unit and player name. local unit, playername = self:_GetPlayerUnitAndName(_unitname) @@ -1677,7 +1746,7 @@ function CARRIERTRAINER:_HandleCollectedResult(playerData, wire) playerData.collectedResultString = newString else playerData.collectedResultString = playerData.collectedResultString .. ", " .. newString - MessageToAll( playerData.callsign .. "'s " .. playerData.passes .. " passes: " .. playerData.collectedResultString .. " (TOTAL: " .. playerData.totalScore .. ")" , 30, "CollectedResult" ) + MessageToAll( playerData.callsign .. "'s " .. playerData.passes .. " passes: " .. playerData.collectedResultString .. " (TOTAL: " .. playerData.totalscore .. ")" , 30, "CollectedResult" ) end local heading=playerData.unit:GetCoordinate():HeadingTo(self.startZone:GetCoordinate()) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 499e0683e..1241f077d 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -4620,7 +4620,7 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) text=text..string.format("Deploying all %d ground assets.", nground) -- Add self request. - self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, WAREHOUSE.Quantity.ALL, nil, nil , 0) + self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, WAREHOUSE.Quantity.ALL, nil, nil , 0, "AutoDefence") else text=text..string.format("No ground assets currently available.") end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index bc05a0db8..cdd6a39dd 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -3034,6 +3034,3 @@ function CONTROLLABLE:IsAirPlane() return nil end - - --- Message APIs \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index e6c6648fd..6c6061e7d 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -325,7 +325,7 @@ end -- So all event listeners will catch the destroy event of this group for each unit in the group. -- To raise these events, provide the `GenerateEvent` parameter. -- @param #GROUP self --- @param #boolean GenerateEvent true if you want to generate a crash or dead event for each unit. +-- @param #boolean GenerateEvent If true, a crash or dead event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered. -- @usage -- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group. -- Helicopter = GROUP:FindByName( "Helicopter" ) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 5487ef3b6..b50a908f4 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -902,22 +902,27 @@ end function UNIT:InAir() self:F2( self.UnitName ) + -- Get DCS unit object. local DCSUnit = self:GetDCSObject() --DCS#Unit if DCSUnit then --- Implementation of workaround. The original code is below. --- This to simulate the landing on buildings. --- local UnitInAir = DCSUnit:inAir() - local UnitInAir = true - local VelocityVec3 = DCSUnit:getVelocity() - local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec - local Coordinate = DCSUnit:getPoint() - local LandHeight = land.getHeight( { x = Coordinate.x, y = Coordinate.z } ) - local Height = Coordinate.y - LandHeight - if Velocity < 1 and Height <= 60 then - UnitInAir = false + -- Get DCS result of whether unit is in air or not. + local UnitInAir = DCSUnit:inAir() + + -- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not. + if UnitInAir==true then + local VelocityVec3 = DCSUnit:getVelocity() + --local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec + local Velocity = UTILS.VecNorm(VelocityVec3) + local Coordinate = DCSUnit:getPoint() + local LandHeight = land.getHeight( { x = Coordinate.x, y = Coordinate.z } ) + local Height = Coordinate.y - LandHeight + if Velocity < 1 and Height <= 60 then + UnitInAir = false + end end + self:T3( UnitInAir ) return UnitInAir end