diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 0503660f1..b5ed1ca9a 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -48,6 +48,7 @@ AIRWING = { ClassName = "AIRWING", Debug = false, + verbose = 0, lid = nil, menu = nil, squadrons = {}, @@ -100,17 +101,17 @@ AIRWING.version="0.1.5" -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- DONE: Add squadrons to warehouse. +-- TODO: Spawn in air or hot. -- TODO: Make special request to transfer squadrons to anther airwing (or warehouse). +-- TODO: Border zone or even multiple zones. +-- TODO: Check that airbase has enough parking spots if a request is BIG. Alternatively, split requests. +-- DONE: Add squadrons to warehouse. -- DONE: Build mission queue. -- DONE: Find way to start missions. -- DONE: Check if missions are done/cancelled. -- DONE: Payloads as resources. --- TODO: Spawn in air or hot. -- DONE: Define CAP zones. -- DONE: Define TANKER zones for refuelling. --- TODO: Border zone or even multiple zones. --- TODO: Check that airbase has enough parking spots if a request is BIG. Alternatively, split requests. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -263,8 +264,8 @@ function AIRWING:NewPayload(Unit, MissionTypes, Npayloads, Unlimited, Priority) table.insert(self.payloads, payload) -- Info - self:I(self.lid..string.format("Adding new payload from unit %s for aircraft type %s: N=%d (unlimited=%s), missions:", payload.unitname, payload.aircrafttype, payload.navail, tostring(payload.unlimited))) - self:I({MissionTypes=payload.missiontypes}) + self:I(self.lid..string.format("Adding new payload from unit %s for aircraft type %s: N=%d (unlimited=%s), prio=%d, missions: %s", + payload.unitname, payload.aircrafttype, payload.navail, tostring(payload.unlimited), payload.priority, table.concat(payload.missiontypes, ", "))) return payload end @@ -280,15 +281,28 @@ end -- @return #AIRWING.Payload Payload table or *nil*. function AIRWING:FetchPayloadFromStock(UnitType, MissionType) + --- Sort payload wrt the following criteria: + -- 1) Highest prio (lower value) is the main selection criterion. + -- 2) If payloads have the same prio, unlimited payloads are preferred over limited ones. + -- 3) If payloads have the same prio _and_ are limited, the abundant one is preferred. local function sortpayloads(a,b) local pA=a --#AIRWING.Payload local pB=b --#AIRWING.Payload - return pA.prioritypB.navail) + end table.sort(self.payloads, sortpayloads) + + --[[ + env.info("FF sorted payloads for mission type X and aircraft type=Y:") + for _,_payload in ipairs(self.payloads) do + local payload=_payload --#AIRWING.Payload + if payload.aircrafttype==UnitType and self:CheckMissionType(MissionType, payload.missiontypes) then + self:I(string.format("FF %s payload for %s: avail=%d prio=%d", MissionType, payload.aircrafttype, payload.navail, payload.priority)) + end + end + ]] - for _,_payload in pairs(self.payloads) do + for _,_payload in ipairs(self.payloads) do local payload=_payload --#AIRWING.Payload @@ -300,6 +314,8 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType) payload.navail=payload.navail-1 end + --self:I(string.format("FETCHING %s payload for %s: avail=%d prio=%d", MissionType, payload.aircrafttype, payload.navail, payload.priority)) + -- Return a copy of the table. return payload end @@ -618,8 +634,12 @@ function AIRWING:onafterStatus(From, Event, To) for i,_squadron in pairs(self.squadrons) do local squadron=_squadron --Ops.Squadron#SQUADRON + local callsign=squadron.callsignName and squadron.callsignName or "N/A" + local modex=squadron.modex and squadron.modex or "N/A" + local skill=squadron.skill and tostring(squadron.skill) or "N/A" + -- Squadron text - text=text..string.format("\n* %s %s", squadron.name, squadron:GetState()) + text=text..string.format("\n* %s %s: Callsign=%s, Modex=%d, Skill=%s", squadron.name, squadron:GetState(), callsign, modex, skill) -- Loop over all assets. for j,_asset in pairs(squadron.assets) do diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index e8c3bd8dc..9fd5bb450 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -159,6 +159,8 @@ AUFTRAG = { missionFraction = 0.5, enrouteTasks = {}, markerID = nil, + startconditions = {}, + stopconditions = {}, } --- Global mission counter. @@ -182,6 +184,8 @@ _AUFTRAGSNR=0 -- @field #string ORBIT Orbit mission. -- @field #string PATROL Similar to CAP but no auto engage targets. -- @field #string RECON Recon mission. +-- @field #string RECOVERYTANKER Recovery tanker mission. +-- @field #string RESCUEHELO Rescue helo. -- @field #string SEAD Suppression/destruction of enemy air defences. -- @field #string STRIKE Strike mission. -- @field #string TANKER Tanker mission. @@ -202,6 +206,8 @@ AUFTRAG.Type={ ORBIT="Orbit", PATROL="Patrol", RECON="Recon", + RECOVERYTANKER="Recovery Tanker", + RESCUEHELO="Rescue Helo", SEAD="SEAD", STRIKE="Strike", TANKER="Tanker", @@ -272,6 +278,7 @@ AUFTRAG.TargetType={ -- @field Wrapper.Positionable#POSITIONABLE Target Target Object. -- @field #string Type Target type: "Group", "Unit", "Static", "Coordinate", "Airbase. -- @field #number Ninital Number of initial targets. +-- @field #number Lifepoints Total life points. --- Mission success. -- @type AUFTRAG.Success @@ -297,6 +304,9 @@ AUFTRAG.version="0.0.9" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Add mission start conditions. +-- TODO: Add recovery tanker mission for boat ops. +-- TODO: Add rescue helo mission for boat ops. -- TODO: Mission success options damaged, destroyed. -- DONE: Mission ROE and ROT. -- TODO: Mission frequency, formation, etc. @@ -313,7 +323,7 @@ AUFTRAG.version="0.0.9" -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Create a new AUFTRAG object. +--- Create a new generic AUFTRAG object. -- @param #AUFTRAG self -- @param #string Type Mission type. -- @return #AUFTRAG self @@ -342,12 +352,9 @@ function AUFTRAG:New(Type) self:SetPriority() self:SetTime() self.engageAsGroup=true - self.missionRepeated=0 self.missionRepeatMax=0 - self.nassets=1 - self.dTevaluate=0 -- FMS start state is PLANNED. @@ -365,7 +372,8 @@ function AUFTRAG:New(Type) self:AddTransition(AUFTRAG.Status.STARTED, "Executing", AUFTRAG.Status.EXECUTING) -- First asset is executing the mission. self:AddTransition("*", "Done", AUFTRAG.Status.DONE) -- All assets have reported that mission is done. - self:AddTransition("*", "Cancel", "*") --AUFTRAG.Status.CANCELLED) -- Command to cancel the mission. + + self:AddTransition("*", "Cancel", "*") -- Command to cancel the mission. self:AddTransition("*", "Success", AUFTRAG.Status.SUCCESS) self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED) @@ -377,13 +385,9 @@ function AUFTRAG:New(Type) self:AddTransition("*", "FlightDead", "*") self:AddTransition("*", "AssetDead", "*") - - --[[ - self:HandleEvent(EVENTS.PilotDead, self._UnitDead) - self:HandleEvent(EVENTS.Ejection, self._UnitDead) - self:HandleEvent(EVENTS.Crash, self._UnitDead) - ]] + + -- Init status update. self:__Status(-1) return self @@ -393,6 +397,27 @@ end -- Create Missions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Create a RESCUE HELO mission. +-- @param #AUFTRAG self +-- @param Wrapper.UnitUNIT Carrier The carrier unit. +-- @return #AUFTRAG self +function AUFTRAG:NewRESCUEHELO(Carrier) + + local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO) + + mission.engageTarget=mission:_TargetFromObject(Target) + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + + mission.missionTask=ENUMS.MissionTask.NOTHING + mission.missionFraction=0.4 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.EvadeFire + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + --- Create an ANTI-SHIP mission. -- @param #AUFTRAG self -- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be passed as a @{Wrapper.Group#GROUP} or @{Wrapper.Unit#UNIT} object. @@ -729,7 +754,7 @@ end --- Create a BOMBING mission. Flight will drop bombs a specified coordinate. -- @param #AUFTRAG self --- @param Core.Point#COORDINATE TargetCoordinate Target coordinate. Can also be specified as a GROUP, UNIT or STATIC object. +-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT or STATIC object. -- @param #number Altitude Engage altitude in feet. Default 25000 ft. -- @return #AUFTRAG self function AUFTRAG:NewBOMBING(Target, Altitude) @@ -1903,6 +1928,56 @@ function AUFTRAG:CountMissionTargets() return N end +--- Get target life points. +-- @param #AUFTRAG self +-- @return #number Number of alive target units. +function AUFTRAG:GetTargetLife() + + local N=0 + + if self.engageTarget then + + if self.engageTarget.Type==AUFTRAG.TargetType.GROUP then + + local target=self.engageTarget.Target --Wrapper.Group#GROUP + + local units=target:GetUnits() + + for _,_unit in pairs(units or {}) do + local unit=_unit --Wrapper.Unit#UNIT + + -- We check that unit is "alive". + if unit and unit:IsAlive() then + N=N+unit:GetLife() + end + end + + elseif self.engageTarget.Type==AUFTRAG.TargetType.UNIT then + + local target=self.engageTarget.Target --Wrapper.Unit#UNIT + + if target and target:IsAlive() then + N=N+target:GetLife() + end + + elseif self.engageTarget.Type==AUFTRAG.TargetType.STATIC then + + local target=self.engageTarget.Target --Wrapper.Static#STATIC + + if target and target:IsAlive() then + N=N+1 --target:GetLife() + end + + elseif self.engageTarget.Type==AUFTRAG.TargetType.AIRBASE then + + -- TODO: any (good) way to tell whether an airbase was "destroyed" or at least damaged? Is :GetLive() working? + + end + end + + return N +end + --- Count alive flight groups assigned for this mission. -- @param #AUFTRAG self -- @return #number Number of alive flight groups. @@ -2251,7 +2326,7 @@ function AUFTRAG:GetDCSMissionTask() -- Count mission targets. self.Ntargets=self:CountMissionTargets() - self:I({Ntargets=self.Ntargets, missiontask=DCStasks}) + self:T({Ntargets=self.Ntargets, missiontask=DCStasks}) -- Return the task. if #DCStasks==1 then diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 41cc96057..781d461dd 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -372,6 +372,7 @@ FLIGHTGROUP.version="0.3.8" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Use new UnitLost event instead of crash/dead. -- TODO: Options EPLRS, Afterburner restrict etc. -- TODO: Add TACAN beacon. -- TODO: Damage? @@ -2872,13 +2873,30 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp wp[#wp+1]=c0:WaypointAir(nil, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, UTILS.KnotsToKmph(SpeedTo), true , nil, {}, "Current Pos") wp[#wp+1]=p0:WaypointAir(nil, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, UTILS.KnotsToKmph(SpeedTo), true , nil, {TaskArrived, TaskHold, TaskKlar}, "Holding Point") - -- Approach point: 10 NN in direction of runway. - local papp=airbase:GetCoordinate():Translate(x1, runway.heading-180):SetAltitude(h1) - wp[#wp+1]=papp:WaypointAirTurningPoint(nil, UTILS.KnotsToKmph(SpeedLand), {}, "Final Approach") + -- Approach point: 10 NN in direction of runway. + if airbase:GetAirbaseCategory()==Airbase.Category.AIRDROME then - -- Okay, it looks like it's best to specify the coordinates not at the airbase but a bit away. This causes a more direct landing approach. - local pland=airbase:GetCoordinate():Translate(x2, runway.heading-180):SetAltitude(h2) - wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand), airbase, {}, "Landing") + --- + -- Airdrome + --- + + local papp=airbase:GetCoordinate():Translate(x1, runway.heading-180):SetAltitude(h1) + wp[#wp+1]=papp:WaypointAirTurningPoint(nil, UTILS.KnotsToKmph(SpeedLand), {}, "Final Approach") + + -- Okay, it looks like it's best to specify the coordinates not at the airbase but a bit away. This causes a more direct landing approach. + local pland=airbase:GetCoordinate():Translate(x2, runway.heading-180):SetAltitude(h2) + wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand), airbase, {}, "Landing") + + elseif airbase:GetAirbaseCategory()==Airbase.Category.SHIP then + + --- + -- Ship + --- + + local pland=airbase:GetCoordinate() + wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand), airbase, {}, "Landing") + + end -- Respawn? if fc or world.event.S_EVENT_KILL then diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 7aed2d8f8..0d6569d54 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -192,7 +192,16 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Set livery. +--- Set livery painted on all squadron aircraft. +-- Note that the livery name in general is different from the name shown in the mission editor. +-- +-- Valid names are the names of the **livery directories**. Check out the folder in your DCS installation: +-- +-- * `DCS World OpenBeta\CoreMods\aircraft\\` +-- * `DCS World OpenBeta\Bazar\Liveries\\` +-- +-- Or personal liveries you have installed somewhere in your saved games folder. +-- -- @param #SQUADRON self -- @param #string LiveryName Name of the livery. -- @return #SQUADRON self @@ -201,9 +210,10 @@ function SQUADRON:SetLivery(LiveryName) return self end ---- Set skill. +--- Set skill level of all squadron team members. -- @param #SQUADRON self -- @param #string Skill Skill of all flights. +-- @usage mysquadron:SetSkill(AI.Skill.EXCELLENT) -- @return #SQUADRON self function SQUADRON:SetSkill(Skill) self.skill=Skill