diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 297082e22..98bce2200 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1609,6 +1609,7 @@ WAREHOUSE = { -- @field #boolean iscargo If true, asset is cargo. If false asset is transport. Nil if in stock. -- @field #number rid The request ID of this asset. -- @field #boolean arrived If true, asset arrived at its destination. +-- @field #number damage Damage of asset group in percent. --- Item of the warehouse queue table. -- @type WAREHOUSE.Queueitem @@ -3821,6 +3822,11 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu asset.spawned=false asset.iscargo=nil asset.arrived=nil + + -- Destroy group if it is alive. + if group:IsAlive()==true then + asset.damage=group:GetDamage() + end -- Add asset to stock. table.insert(self.stock, asset) @@ -3991,6 +3997,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, asset.skill=skill asset.assignment=assignment asset.spawned=false + asset.damage=0 asset.spawngroupname=string.format("%s_AID-%d", templategroupname, asset.uid) if i==1 then diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index e87de00e1..2d2d28ae7 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -66,7 +66,7 @@ -- At this point the airwing does not have any assets (aircraft). In order to add these, one needs to first define SQUADRONS. -- -- VFA151=SQUADRON:New("F-14 Group", 8, "VFA-151 (Vigilantes)") --- VFA151:AddMissionCapability({AUFTRAG.Type.PATROL, AUFTRAG.Type.INTERCEPT}) +-- VFA151:AddMissionCapability({AUFTRAG.Type.GCCAP, AUFTRAG.Type.INTERCEPT}) -- -- airwing:AddSquadron(VFA151) -- @@ -78,8 +78,8 @@ -- defined in the Mission Editor. -- -- -- F-14 payloads for CAP and INTERCEPT. Phoenix are first, sparrows are second choice. --- airwing:NewPayload(GROUP:FindByName("F-14 Payload AIM-54C"), 2, {AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROL}, 80) --- airwing:NewPayload(GROUP:FindByName("F-14 Payload AIM-7M"), 20, {AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROL}) +-- airwing:NewPayload(GROUP:FindByName("F-14 Payload AIM-54C"), 2, {AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.GCCAP}, 80) +-- airwing:NewPayload(GROUP:FindByName("F-14 Payload AIM-7M"), 20, {AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.GCCAP}) -- -- This will add two AIM-54C and 20 AIM-7M payloads. -- @@ -127,6 +127,7 @@ AIRWING = { -- @field #AIRWING.Payload payload The payload of the asset. -- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flightgroup object. -- @field #string squadname Name of the squadron this asset belongs to. +-- @field #number Treturned Time stamp when asset returned to the airwing. -- @extends Functional.Warehouse#WAREHOUSE.Assetitem --- Payload data. @@ -284,26 +285,32 @@ function AIRWING:NewPayload(Unit, Npayloads, MissionTypes, Performance) Performance=Performance or 50 if type(Unit)=="string" then - Unit=UNIT:FindByName(Unit) + local name=Unit + env.info("unit as string "..Unit) + Unit=UNIT:FindByName(name) if not Unit then - Unit=GROUP:FindByName(Unit) + env.info("no UNIT trying group") + Unit=GROUP:FindByName(name) + if not Unit then + env.info("no GROUP either!") + end end end - -- If a GROUP object was given, get the first unit. - if Unit:IsInstanceOf("GROUP") then - Unit=Unit:GetUnit(1) - end - - -- Ensure Missiontypes is a table. - if MissionTypes and type(MissionTypes)~="table" then - MissionTypes={MissionTypes} - end - if Unit then + + -- If a GROUP object was given, get the first unit. + if Unit:IsInstanceOf("GROUP") then + Unit=Unit:GetUnit(1) + end + + -- Ensure Missiontypes is a table. + if MissionTypes and type(MissionTypes)~="table" then + MissionTypes={MissionTypes} + end - local payload={} --#AIRWING.Payload - + -- Create payload. + local payload={} --#AIRWING.Payload payload.unitname=Unit:GetName() payload.aircrafttype=Unit:GetTypeName() payload.pylons=Unit:GetTemplatePayload() @@ -338,8 +345,10 @@ function AIRWING:NewPayload(Unit, Npayloads, MissionTypes, Performance) table.insert(self.payloads, payload) return payload + end + self:E(self.lid.."ERROR: No UNIT found to create PAYLOAD!") return nil end @@ -771,7 +780,7 @@ function AIRWING:onafterStatus(From, Event, To) -- General info: -- TODO: assets total - local text=string.format("Status %s: missions=%d, payloads=%d (%d), squads=%d", fsmstate, nmissions, #self.payloads, Npayloads, #self.squadrons) + local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d", fsmstate, nmissions, Npayloads, #self.payloads, #self.squadrons) self:I(self.lid..text) ------------------ @@ -898,7 +907,7 @@ end -- @return #AIRWING self function AIRWING:CheckCAP() - local Ncap=self:CountMissionsInQueue({AUFTRAG.Type.PATROL, AUFTRAG.Type.INTERCEPT}) + local Ncap=self:CountMissionsInQueue({AUFTRAG.Type.GCCAP, AUFTRAG.Type.INTERCEPT}) for i=1,self.nflightsCAP-Ncap do @@ -906,7 +915,7 @@ function AIRWING:CheckCAP() local altitude=patrol.altitude+1000*patrol.noccupied - local missionCAP=AUFTRAG:NewPATROL(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg) + local missionCAP=AUFTRAG:NewGCCAP(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg) missionCAP.patroldata=patrol @@ -1068,7 +1077,7 @@ function AIRWING:GetTankerForFlight(flightgroup) end ---- Get next mission. +--- Check if mission is not over and ready to cancel. -- @param #AIRWING self function AIRWING:_CheckMissions() @@ -1076,12 +1085,8 @@ function AIRWING:_CheckMissions() for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG - if mission:IsNotOver() then - - if mission:IsReadyToCancel() then - mission:Cancel() - end - + if mission:IsNotOver() and mission:IsReadyToCancel() then + mission:Cancel() end end @@ -1157,7 +1162,7 @@ function AIRWING:_GetNextMission() -- Another check. if #assets #Assets then self:I(self.lid..string.format("INFO: Not enough assets available! Got %d but need at least %d", #Assets, Mission.nassets)) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 0ef2d07e1..cb7944935 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -51,7 +51,7 @@ -- @field #number orbitLeg Length of orbit leg in meters. -- @field Core.Point#COORDINATE orbitRaceTrack Race-track orbit coordinate. -- --- @field #AUFTRAG.TargetData engageTarget Target data to engage. +-- @field Ops.Target#TARGET engageTarget Target data to engage. -- -- @field Core.Zone#ZONE_RADIUS engageZone *Circular* engagement zone. -- @field #table engageTargetTypes Table of target types that are engaged in the engagement zone. @@ -98,11 +98,6 @@ -- @field #number missionRepeated Number of times mission was repeated. -- @field #number missionRepeatMax Number of times mission is repeated if failed. -- --- @field #number radioFreq Mission radio frequency in MHz. --- @field #number radioModu Mission radio modulation (0=AM and 1=FM). --- @field #number tacanChannel Mission TACAN channel. --- @field #number tacanMorse Mission TACAN morse code. --- -- @field Ops.OpsGroup#OPSGROUP.Radio radio Radio freq and modulation. -- @field Ops.OpsGroup#OPSGROUP.Beacon tacan TACAN setting. -- @field Ops.OpsGroup#OPSGROUP.Beacon icls ICLS setting. @@ -188,9 +183,9 @@ -- -- An orbit mission can be created with the @{#AUFTRAG.NewORBIT}() function. -- --- ## PATROL +-- ## GCCAP -- --- An patrol mission can be created with the @{#AUFTRAG.NewPATROL}() function. +-- An patrol mission can be created with the @{#AUFTRAG.NewGCCAP}() function. -- -- ## RECON -- @@ -293,7 +288,7 @@ _AUFTRAGSNR=0 -- @field #string FERRY Ferry flight mission. -- @field #string INTERCEPT Intercept mission. -- @field #string ORBIT Orbit mission. --- @field #string PATROL Similar to CAP but no auto engage targets. +-- @field #string GCCAP Similar to CAP but no auto engage targets. -- @field #string RECON Recon mission. -- @field #string RECOVERYTANKER Recovery tanker mission. Not implemented yet. -- @field #string RESCUEHELO Rescue helo. @@ -316,7 +311,7 @@ AUFTRAG.Type={ FERRY="Ferry Flight", INTERCEPT="Intercept", ORBIT="Orbit", - PATROL="Patrol", + GCCAP="Patrol", RECON="Recon", RECOVERYTANKER="Recovery Tanker", RESCUEHELO="Rescue Helo", @@ -632,7 +627,7 @@ function AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) return mission end ---- Create a PATROL mission. +--- Create a GCCAP mission. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to orbit. -- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. @@ -640,13 +635,13 @@ end -- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees. -- @param #number Leg Length of race-track in NM. Default 10 NM. -- @return #AUFTRAG self -function AUFTRAG:NewPATROL(Coordinate, Altitude, Speed, Heading, Leg) +function AUFTRAG:NewGCCAP(Coordinate, Altitude, Speed, Heading, Leg) -- Create ORBIT first. local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - -- Mission type PATROL. - mission.type=AUFTRAG.Type.PATROL + -- Mission type GCCAP. + mission.type=AUFTRAG.Type.GCCAP mission:_SetLogID() @@ -671,7 +666,7 @@ function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSyst -- Create ORBIT first. local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - -- Mission type PATROL. + -- Mission type TANKER. mission.type=AUFTRAG.Type.TANKER mission:_SetLogID() @@ -701,7 +696,7 @@ function AUFTRAG:NewAWACS(Coordinate, Altitude, Speed, Heading, Leg) -- Create ORBIT first. local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - -- Mission type PATROL. + -- Mission type AWACS. mission.type=AUFTRAG.Type.AWACS mission:_SetLogID() @@ -1082,7 +1077,7 @@ function AUFTRAG:NewRESCUEHELO(Carrier) local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO) - mission:_TargetFromObject(Carrier) + self.carrier=Carrier -- Mission options: mission.missionTask=ENUMS.MissionTask.NOTHING @@ -1157,6 +1152,31 @@ function AUFTRAG:NewARTY(Target, Nshots, Radius) return mission end +--- Create a mission to attack a group. Mission type is automatically chosen from the group category. +-- @param #AUFTRAG self +-- @param Ops.Target#TARGET Target The target. +-- @return #AUFTRAG self +function AUFTRAG:NewTARGET(Target) + + local mission=nil --#AUFTRAG + + if Target.category==TARGET.Category.GROUND then + + + elseif Target.category==TARGET.Category.AIRCRAFT then + + elseif Target.category==TARGET.Category.AIRBASE then + + elseif Target.category==TARGET.Category.COORDINATE then + + end + + + if mission then + mission:SetPriority(10, true) + end + +end --- Create a mission to attack a group. Mission type is automatically chosen from the group category. -- @param #AUFTRAG self @@ -1882,12 +1902,6 @@ function AUFTRAG:Evaluate() -- Assume success and check if any failed condition applies. local failed=false - -- Any success condition true? - local successCondition=self:EvalConditionsAny(self.conditionSuccess) - - -- Any failure condition true? - local failureCondition=self:EvalConditionsAny(self.conditionFailure) - -- Target damage in %. local targetdamage=self:GetTargetDamage() @@ -1931,6 +1945,13 @@ function AUFTRAG:Evaluate() end + + -- Any success condition true? + local successCondition=self:EvalConditionsAny(self.conditionSuccess) + + -- Any failure condition true? + local failureCondition=self:EvalConditionsAny(self.conditionFailure) + if failureCondition then failed=true elseif successCondition then @@ -2217,8 +2238,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Ops.OpsGroup#OPSGROUP FlightGroup -function AUFTRAG:onafterScheduled(From, Event, To, FlightGroup) +function AUFTRAG:onafterScheduled(From, Event, To) self.status=AUFTRAG.Status.SCHEDULED self:T(self.lid..string.format("New mission status=%s", self.status)) end @@ -2264,6 +2284,7 @@ end -- @param #string To To state. -- @param Ops.OpsGroup#OPSGROUP OpsGroup The ops group that is dead now. function AUFTRAG:onafterElementDestroyed(From, Event, To, OpsGroup, Element) + -- Increase number of own casualties. self.Ncasualties=self.Ncasualties+1 end @@ -2499,6 +2520,170 @@ function AUFTRAG:onafterStop(From, Event, To) end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Target Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create target data from a given object. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC. +function AUFTRAG:_TargetFromObject(Object) + + if not self.engageTarget then + + self.engageTarget=TARGET:New(Object) + + else + + -- Target was already specified elsewhere. + + end + + -- TODO: get rid of this. + self.Ntargets=self.engageTarget.Ntargets0 + + -- Debug info. + self:T(self.lid..string.format("Mission Target %s Type=%s, Ntargets=%d, Lifepoints=%d", self.engageTarget.lid, self.engageTarget.lid, self.Ntargets, self.engageTarget:GetLife())) + + return self +end + + +--- Count alive mission targets. +-- @param #AUFTRAG self +-- @param #AUFTRAG.TargetData Target (Optional) The target object. +-- @return #number Number of alive target units. +function AUFTRAG:CountMissionTargets(Target) + + if self.engageTarget then + return self.engageTarget:CountTargets() + else + return 0 + end + +end + +--- Get target life points. +-- @param #AUFTRAG self +-- @return #number Number of initial life points when mission was planned. +function AUFTRAG:GetTargetInitialLife() + local target=self:GetTargetData() + if target then + return target.life0 + else + return 0 + end +end + +--- Get target damage. +-- @param #AUFTRAG self +-- @return #number Damage in percent. +function AUFTRAG:GetTargetDamage() + local target=self:GetTargetData() + if target then + return target:GetDamage() + else + return 0 + end +end + + +--- Get target life points. +-- @param #AUFTRAG self +-- @return #number Life points of target. +function AUFTRAG:GetTargetLife() + local target=self:GetTargetData() + if target then + return target:GetLife() + else + return 0 + end +end + +--- Get target. +-- @param #AUFTRAG self +-- @return Ops.Target#TARGET The target object. Could be many things. +function AUFTRAG:GetTargetData() + return self.engageTarget +end + +--- Get mission objective object. Could be many things depending on the mission type. +-- @param #AUFTRAG self +-- @return Wrapper.Positionable#POSITIONABLE The target object. Could be many things. +function AUFTRAG:GetObjective() + return self:GetTargetData().Target +end + +--- Get type of target. +-- @param #AUFTRAG self +-- @return #string The target type. +function AUFTRAG:GetTargetType() + return self:GetTargetData().Type +end + +--- Get 2D vector of target. +-- @param #AUFTRAG self +-- @return DCS#VEC2 The target 2D vector or *nil*. +function AUFTRAG:GetTargetVec2() + local coord=self:GetTargetCoordinate() + if coord then + return coord:GetVec2() + end + return nil +end + +--- Get coordinate of target. +-- @param #AUFTRAG self +-- @return Core.Point#COORDINATE The target coordinate or *nil*. +function AUFTRAG:GetTargetCoordinate() + + if self.transportPickup then + + -- Special case where we defined a + return self.transportPickup + + elseif self.engageTarget then + + return self.engageTarget:GetCoordinate() + + else + self:E(self.lid.."ERROR: Cannot get target coordinate!") + end + + return nil +end + +--- Get name of the target. +-- @param #AUFTRAG self +-- @return #string Name of the target or "N/A". +function AUFTRAG:GetTargetName() + + if self.engageTarget.Target then + return self.engageTarget.Name + end + + return "N/A" +end + + +--- Get distance to target. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE FromCoord The coordinate from which the distance is measured. +-- @return #number Distance in meters or 0. +function AUFTRAG:GetTargetDistance(FromCoord) + + local TargetCoord=self:GetTargetCoordinate() + + if TargetCoord and FromCoord then + return TargetCoord:Get2DDistance(FromCoord) + else + self:E(self.lid.."ERROR: TargetCoord or FromCoord does not exist in AUFTRAG:GetTargetDistance() function! Returning 0") + end + + return 0 +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2554,229 +2739,6 @@ function AUFTRAG:GetAssetByName(Name) return nil end - ---- Count alive mission targets. --- @param #AUFTRAG self --- @param #AUFTRAG.TargetData Target (Optional) The target object. --- @return #number Number of alive target units. -function AUFTRAG:CountMissionTargets(Target) - - local N=0 - - Target=Target or self:GetTargetData() - - if Target then - - if Target.Type==AUFTRAG.TargetType.GROUP then - - local target=Target.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" and has health >1. Somtimes units get heavily damanged but are still alive. - -- TODO: here I could introduce and count that if units have only health < 50% if mission objective is to just "damage" the units. - if unit and unit:IsAlive() and unit:GetLife()>1 then - N=N+1 - end - end - - elseif Target.Type==AUFTRAG.TargetType.UNIT then - - local target=Target.Target --Wrapper.Unit#UNIT - - if target and target:IsAlive() and target:GetLife()>1 then - N=N+1 - end - - elseif Target.Type==AUFTRAG.TargetType.STATIC then - - local target=Target.Target --Wrapper.Static#STATIC - - if target and target:IsAlive() then - N=N+1 - end - - elseif Target.Type==AUFTRAG.TargetType.AIRBASE then - - -- TODO: any (good) way to tell whether an airbase was "destroyed" or at least damaged? Is :GetLive() working? - - elseif Target.Type==AUFTRAG.TargetType.COORDINATE then - - -- No target! - - elseif Target.Type==AUFTRAG.TargetType.SETGROUP then - - for _,_group in pairs(Target.Target.Set or {}) do - local group=_group --Wrapper.Group#GROUP - - local units=group: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() and unit:GetLife()>1 then - N=N+1 - end - end - - end - - elseif Target.Type==AUFTRAG.TargetType.SETUNIT then - - for _,_unit in pairs(Target.Target.Set or {}) do - local unit=_unit --Wrapper.Unit#UNIT - - -- We check that unit is "alive". - if unit and unit:IsAlive() and unit:GetLife()>1 then - N=N+1 - end - - end - - else - self:E("ERROR unknown target type") - end - end - - return N -end - ---- Get target life points. --- @param #AUFTRAG self --- @return #number Number of initial life points when mission was planned. -function AUFTRAG:GetTargetInitialLife() - return self:GetTargetData().Lifepoints -end - ---- Get target damage. --- @param #AUFTRAG self --- @return #number Damage in percent. -function AUFTRAG:GetTargetDamage() - local target=self:GetTargetData() - local life=self:GetTargetLife()/self:GetTargetInitialLife() - local damage=1-life - return damage*100 -end - - ---- Get target life points. --- @param #AUFTRAG self --- @return #number Life points of target. -function AUFTRAG:GetTargetLife() - return self:_GetTargetLife(nil, false) -end - ---- Get target life points. --- @param #AUFTRAG self --- @param #AUFTRAG.TargetData Target (Optional) The target object. --- @param #boolean Healthy Get the life points of the healthy target. --- @return #number Life points of target. -function AUFTRAG:_GetTargetLife(Target, Healthy) - - local N=0 - - Target=Target or self:GetTargetData() - - local function _GetLife(unit) - local unit=unit --Wrapper.Unit#UNIT - if Healthy then - local life=unit:GetLife() - local life0=unit:GetLife0() - - return math.max(life, life0) - else - return unit:GetLife() - end - end - - if Target then - - if Target.Type==AUFTRAG.TargetType.GROUP then - - local target=Target.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+_GetLife(unit) - end - end - - elseif Target.Type==AUFTRAG.TargetType.UNIT then - - local target=Target.Target --Wrapper.Unit#UNIT - - if target and target:IsAlive() then - N=N+_GetLife(target) - end - - elseif Target.Type==AUFTRAG.TargetType.STATIC then - - local target=Target.Target --Wrapper.Static#STATIC - - -- Statics are alive or not. - if target and target:IsAlive() then - N=N+1 --_GetLife(target) - else - N=N+0 - end - - elseif Target.Type==AUFTRAG.TargetType.AIRBASE then - - -- TODO: any (good) way to tell whether an airbase was "destroyed" or at least damaged? Is :GetLive() working? - N=N+1 - - elseif Target.Type==AUFTRAG.TargetType.COORDINATE then - - -- A coordinate does not live. - N=N+1 - - elseif Target.Type==AUFTRAG.TargetType.SETGROUP then - - for _,_group in pairs(Target.Target.Set or {}) do - local group=_group --Wrapper.Group#GROUP - - local units=group: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+_GetLife(unit) - end - end - - end - - elseif Target.Type==AUFTRAG.TargetType.SETUNIT then - - for _,_unit in pairs(Target.Target.Set or {}) do - local unit=_unit --Wrapper.Unit#UNIT - - -- We check that unit is "alive". - if unit and unit:IsAlive() then - N=N+_GetLife(unit) - end - - end - - else - self:E(self.lid.."ERROR unknown target type") - end - end - - return N -end - --- Count alive flight groups assigned for this mission. -- @param #AUFTRAG self -- @return #number Number of alive flight groups. @@ -2791,109 +2753,6 @@ function AUFTRAG:CountOpsGroups() return N end ---- Get coordinate of target. --- @param #AUFTRAG self --- @return #AUFTRAG.TargetData The target object. Could be many things. -function AUFTRAG:GetTargetData() - return self.engageTarget -end - ---- Get mission objective object. Could be many things depending on the mission type. --- @param #AUFTRAG self --- @return Wrapper.Positionable#POSITIONABLE The target object. Could be many things. -function AUFTRAG:GetObjective() - return self:GetTargetData().Target -end - ---- Get type of target. --- @param #AUFTRAG self --- @return #string The target type. -function AUFTRAG:GetTargetType() - return self:GetTargetData().Type -end - ---- Get 2D vector of target. --- @param #AUFTRAG self --- @return DCS#VEC2 The target 2D vector or *nil*. -function AUFTRAG:GetTargetVec2() - local coord=self:GetTargetCoordinate() - if coord then - return coord:GetVec2() - end - return nil -end - ---- Get coordinate of target. --- @param #AUFTRAG self --- @return Core.Point#COORDINATE The target coordinate or *nil*. -function AUFTRAG:GetTargetCoordinate() - - if self.transportPickup then - - -- Special case where we defined a - return self.transportPickup - - else - - local target - - if self:GetTargetType()==AUFTRAG.TargetType.COORDINATE then - - -- Here the objective itself is a COORDINATE. - return self:GetObjective() - - elseif self:GetTargetType()==AUFTRAG.TargetType.SETGROUP then - - -- Return the first group in the set. - -- TODO: does this only return ALIVE groups?! - return self:GetObjective():GetFirst():GetCoordinate() - - elseif self:GetTargetType()==AUFTRAG.TargetType.SETUNIT then - - -- Return the first unit in the set. - -- TODO: does this only return ALIVE units?! - return self:GetObjective():GetFirst():GetCoordinate() - - else - - -- In all other cases the GetCoordinate() function should work. - return self:GetObjective():GetCoordinate() - - end - end - - return nil -end - ---- Get name of the target. --- @param #AUFTRAG self --- @return #string Name of the target or "N/A". -function AUFTRAG:GetTargetName() - - if self.engageTarget.Target then - return self.engageTarget.Name - end - - return "N/A" -end - - ---- Get distance to target. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE FromCoord The coordinate from which the distance is measured. --- @return #number Distance in meters or 0. -function AUFTRAG:GetTargetDistance(FromCoord) - - local TargetCoord=self:GetTargetCoordinate() - - if TargetCoord and FromCoord then - return TargetCoord:Get2DDistance(FromCoord) - else - self:E(self.lid.."ERROR: TargetCoord or FromCoord does not exist in AUFTRAG:GetTargetDistance() function! Returning 0") - end - - return 0 -end --- Get coordinate of target. First unit/group of the set is used. -- @param #AUFTRAG self @@ -3100,10 +2959,10 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- Done below as also other mission types use the orbit task. - elseif self.type==AUFTRAG.Type.PATROL then + elseif self.type==AUFTRAG.Type.GCCAP then -------------------- - -- PATROL Mission -- + -- GCCAP Mission -- -------------------- -- Done below as also other mission types use the orbit task. @@ -3177,7 +3036,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} - param.unitname=self:GetTargetName() + param.unitname=self.carrier:GetName() param.offsetX=200 param.offsetZ=240 param.altitude=70 @@ -3207,7 +3066,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) if self.type==AUFTRAG.Type.ORBIT or self.type==AUFTRAG.Type.CAP or self.type==AUFTRAG.Type.CAS or - self.type==AUFTRAG.Type.PATROL or + self.type==AUFTRAG.Type.GCCAP or self.type==AUFTRAG.Type.AWACS or self.type==AUFTRAG.Type.TANKER then @@ -3237,136 +3096,35 @@ end --- Get DCS task table for an attack group or unit task. -- @param #AUFTRAG self --- @param #AUFTRAG.TargetData target Target data. +-- @param Ops.Target#TARGET Target Target data. -- @param #table DCStasks DCS DCS tasks table to which the task is added. -- @return DCS#Task The DCS task table. -function AUFTRAG:_GetDCSAttackTask(target, DCStasks) +function AUFTRAG:_GetDCSAttackTask(Target, DCStasks) - local DCStask=nil + DCStasks=DCStasks or {} + + for _,_target in pairs(Target.targets) do + local target=_target --Ops.Target#TARGET.Object - if target.Type==AUFTRAG.TargetType.GROUP then - - DCStask=CONTROLLABLE.TaskAttackGroup(nil, target.Target, self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageAsGroup) - - table.insert(DCStasks, DCStask) + if target.Type==TARGET.ObjectType.GROUP then - elseif target.Type==AUFTRAG.TargetType.UNIT or target.Type==AUFTRAG.TargetType.STATIC then - - DCStask=CONTROLLABLE.TaskAttackUnit(nil, target.Target, self.engageAsGroup, self.WeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) - - table.insert(DCStasks, DCStask) - - elseif target.Type==AUFTRAG.TargetType.SETGROUP then - - -- Add all groups. - for _,group in pairs(target.Target.Set or {}) do - DCStask=CONTROLLABLE.TaskAttackGroup(nil, group, self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageAsGroup) + local DCStask=CONTROLLABLE.TaskAttackGroup(nil, target.Object, self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageAsGroup) + table.insert(DCStasks, DCStask) - end - - elseif target.Type==AUFTRAG.TargetType.SETUNIT then - - -- Add tasks to attack all units. - for _,unit in pairs(target.Target.Set or {}) do - DCStask=CONTROLLABLE.TaskAttackUnit(nil, unit, self.engageAsGroup, self.WeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) + + elseif target.Type==TARGET.ObjectType.UNIT or target.Type==TARGET.ObjectType.STATIC then + + local DCStask=CONTROLLABLE.TaskAttackUnit(nil, target.Object, self.engageAsGroup, self.WeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) + table.insert(DCStasks, DCStask) + end - + end - + return DCStasks end ---- Create target data from a given object. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC. --- @return #AUFTRAG.TargetData Target. -function AUFTRAG:_TargetFromObject(Object) - - local target={} --#AUFTRAG.TargetData - - -- The object. - target.Target=Object - - if Object:IsInstanceOf("GROUP") then - - target.Type=AUFTRAG.TargetType.GROUP - - local object=Object --Wrapper.Group#GROUP - - target.Name=object:GetName() - - elseif Object:IsInstanceOf("UNIT") then - - target.Type=AUFTRAG.TargetType.UNIT - - local object=Object --Wrapper.Unit#UNIT - - target.Name=object:GetName() - - elseif Object:IsInstanceOf("STATIC") then - - target.Type=AUFTRAG.TargetType.STATIC - - target.Name=Object:GetName() - - elseif Object:IsInstanceOf("COORDINATE") then - - target.Type=AUFTRAG.TargetType.COORDINATE - - local object=Object --Core.Point#COORDINATE - - target.Name=object:ToStringLLDMS() - - elseif Object:IsInstanceOf("AIRBASE") then - - target.Type=AUFTRAG.TargetType.AIRBASE - - local object=Object --Wrapper.Airbase#AIRBASE - - target.Name=object:GetName() - - elseif Object:IsInstanceOf("SET_GROUP") then - - target.Type=AUFTRAG.TargetType.SETGROUP - - local object=Object --Core.Set#SET_GROUP - - target.Name=object:GetFirst():GetName() - - elseif Object:IsInstanceOf("SET_UNIT") then - - target.Type=AUFTRAG.TargetType.SETUNIT - - local object=Object --Core.Set#SET_UNIT - - target.Name=object:GetFirst():GetName() - - else - self:E(self.lid.."ERROR: Unknown object given as target. Needs to be a GROUP, UNIT, STATIC, COORDINATE") - return nil - end - - - -- Number of initial targets. - local Ninitial=self:CountMissionTargets(target) - - -- Initial total life point. - local Lifepoints=self:_GetTargetLife(target, true) - - -- Set engage Target. - self.engageTarget=target - self.engageTarget.Ninital=Ninitial - self.engageTarget.Lifepoints=Lifepoints - - -- TODO: get rid of this. - self.Ntargets=Ninitial - - -- Debug info. - self:T(self.lid..string.format("Mission Target %s Type=%s, Ntargets=%d, Lifepoints=%d", target.Name, target.Type, Ninitial, Lifepoints)) - - return target -end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index f5300d295..34aaaa818 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -2624,6 +2624,9 @@ function FLIGHTGROUP:AddElementByName(unitname) element.status=OPSGROUP.ElementStatus.INUTERO element.group=unit:GetGroup() + -- TODO: this is wrong when grouping is used! + local unittemplate=element.unit:GetTemplate() + element.modex=element.unit:GetTemplate().onboard_num element.skill=element.unit:GetTemplate().skill element.pylons=element.unit:GetTemplatePylons() @@ -2642,8 +2645,8 @@ function FLIGHTGROUP:AddElementByName(unitname) element.ai=true end - local text=string.format("Adding element %s: status=%s, skill=%s, modex=%s, fuelmass=%.1f (%d %%), category=%d, categoryname=%s, callsign=%s, ai=%s", - element.name, element.status, element.skill, element.modex, element.fuelmass, element.fuelrel, element.category, element.categoryname, element.callsign, tostring(element.ai)) + local text=string.format("Adding element %s: status=%s, skill=%s, modex=%s, fuelmass=%.1f (%d), category=%d, categoryname=%s, callsign=%s, ai=%s", + element.name, element.status, element.skill, element.modex, element.fuelmass, element.fuelrel*100, element.category, element.categoryname, element.callsign, tostring(element.ai)) self:I(self.lid..text) -- Add element to table. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 2475b5677..0ce61fdc7 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1437,7 +1437,7 @@ end function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- Debug message. - local text=string.format("Task %s ID=%d execute.", tostring(Task.description), Task.id) + local text=string.format("Task %s ID=%d execute", tostring(Task.description), Task.id) MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) self:I(self.lid..text) diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 499863a46..88261aa7c 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -26,6 +26,8 @@ -- @field #number ngrouping User defined number of units in the asset group. -- @field #table assets Squadron assets. -- @field #table missiontypes Capabilities (mission types and performances) of the squadron. +-- @field #number maintenancetime Time in seconds needed for maintenance of a returned flight. +-- @field #number repairtime Time in seconds for each -- @field #string livery Livery of the squadron. -- @field #number skill Skill of squadron members. -- @field #number modex Modex. @@ -67,6 +69,8 @@ SQUADRON = { aircrafttype = nil, assets = {}, missiontypes = {}, + repairtime = 0, + maintenancetime= 0, livery = nil, skill = nil, modex = nil, @@ -233,6 +237,17 @@ function SQUADRON:SetSkill(Skill) return self end +--- Set maintenance and repair time. +-- @param #SQUADRON self +-- @param #number MaintenanceTime Time in minutes it takes until a flight is combat ready again. Default is 0 min. +-- @param #number RepairTime Time in minutes it takes to repair a flight for each percent damage taken. Default is 0 min. +-- @return #SQUADRON self +function SQUADRON:SetMaintenanceTime(MaintenanceTime, RepairTime) + self.maintenancetime=MaintenanceTime and MaintenanceTime*60 or 0 + self.repairtime=RepairTime and RepairTime*60 or 0 + return self +end + --- Set radio frequency and modulation the squad uses. -- @param #SQUADRON self -- @param #number Frequency Radio frequency in MHz. Default 251 MHz. @@ -683,12 +698,12 @@ function SQUADRON:RecruitAssets(Mission) -- Asset is already on a mission. --- - -- Check if this asset is currently on a PATROL mission (STARTED or EXECUTING). - if self.airwing:IsAssetOnMission(asset, AUFTRAG.Type.PATROL) and Mission.type==AUFTRAG.Type.INTERCEPT then + -- Check if this asset is currently on a GCCAP mission (STARTED or EXECUTING). + if self.airwing:IsAssetOnMission(asset, AUFTRAG.Type.GCCAP) and Mission.type==AUFTRAG.Type.INTERCEPT then -- Check if the payload of this asset is compatible with the mission. - -- Note: we do not check the payload as an asset that is on a PATROL mission should be able to do an INTERCEPT as well! - self:I(self.lid.."Adding asset on PATROL mission for an INTERCEPT mission") + -- Note: we do not check the payload as an asset that is on a GCCAP mission should be able to do an INTERCEPT as well! + self:I(self.lid.."Adding asset on GCCAP mission for an INTERCEPT mission") table.insert(assets, asset) end @@ -696,7 +711,7 @@ function SQUADRON:RecruitAssets(Mission) else --- - -- Asset as no current mission + -- Asset as NO current mission --- if asset.spawned then @@ -744,7 +759,7 @@ function SQUADRON:RecruitAssets(Mission) --- -- Check that asset is not already requested for another mission. - if Npayloads>0 and not asset.requested then + if Npayloads>0 and self:IsRepaired(asset) and (not asset.requested) then -- Add this asset to the selection. table.insert(assets, asset) @@ -761,6 +776,26 @@ function SQUADRON:RecruitAssets(Mission) return assets end +--- Checks if a mission type is contained in a table of possible types. +-- @param #SQUADRON self +-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset. +-- @return #boolean If true, the requested mission type is part of the possible mission types. +function SQUADRON:IsRepaired(Asset) + + if Asset.Treturned then + local Tnow=timer.getAbsTime() + if Asset.Treturned+self.maintenancetime>=Tnow then + return true + else + return false + end + + else + return true + end + +end + --- Checks if a mission type is contained in a table of possible types. -- @param #SQUADRON self diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 254f4d977..ffbaca4ab 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -16,11 +16,15 @@ -- @type TARGET -- @field #string ClassName Name of the class. -- @field #boolean Debug Debug mode. Messages to all about status. +-- @field #number verbose Verbosity level. -- @field #string lid Class id string for output to DCS log file. -- @field #table targets Table of target objects. -- @field #number targetcounter Running number to generate target object IDs. -- @field #number life Total life points on last status update. -- @field #number life0 Total life points of completely healthy targets. +-- @field #number threatlevel0 Initial threat level. +-- @field #number category Target category (Ground, Air, Sea). +-- @field #number Ntargets0 Number of initial targets. -- @extends Core.Fsm#FSM --- **It is far more important to be able to hit the target than it is to haggle over who makes a weapon or who pulls a trigger** -- Dwight D. Eisenhower @@ -38,11 +42,14 @@ TARGET = { ClassName = "TARGET", Debug = nil, + verbose = 0, lid = nil, targets = {}, targetcounter = 0, life = 0, life0 = 0, + Ntargets0 = 0, + threatlevel0 = 0 } @@ -60,7 +67,23 @@ TARGET.ObjectType={ AIRBASE="Airbase", } ---- Type. + +--- Category. +-- @type TARGET.Category +-- @field #string AIRCRAFT +-- @field #string GROUND +-- @field #string NAVAL +-- @field #string AIRBASE +-- @field #string COORDINATE +TARGET.Category={ + AIRCRAFT="Aircraft", + GROUND="Grund", + NAVAL="Naval", + AIRBASE="Airbase", + COORDINATE="Coordinate", +} + +--- Object status. -- @type TARGET.ObjectStatus -- @field #string ALIVE Object is alive. -- @field #string DEAD Object is dead. @@ -105,10 +128,15 @@ function TARGET:New(TargetObject) -- Increase counter. _TARGETID=_TARGETID+1 - - self.lid=string.format("TARGET #%03d | ", _TARGETID) - + + -- Add object. self:AddObject(TargetObject) + + local Target=self.targets[1] --#TARGET.Object + + self.category=self:GetTargetCategory(Target) + + self.lid=string.format("TARGET #%03d %s | ", _TARGETID, self.category) -- Start state. self:SetStartState("Stopped") @@ -179,30 +207,23 @@ end -- @param #TARGET self -- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE. function TARGET:AddObject(Object) + + if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") then + + --- + -- Sets + --- - if Object:IsInstanceOf("GROUP") then - - local group=Object --Wrapper.Group#GROUP - - local units=group:GetUnits() - - for _,unit in pairs(units) do - self:_AddObject(unit) - end - - elseif Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") then - local set=Object --Core.Set#SET_GROUP for _,object in pairs(set.Set) do self:AddObject(object) - end - + end else --- - -- Units, Statics, Airbases, Coordinates + -- Groups, Units, Statics, Airbases, Coordinates --- self:_AddObject(Object) @@ -265,7 +286,7 @@ function TARGET:onafterStatus(From, Event, To) end -- Log output. - local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f", fsmstate, self:CountTargets(), #self.targets, self:GetLife(), self:GetLife0(), self:GetDamage()) + local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f", fsmstate, self:CountTargets(), self.Ntargets0, self:GetLife(), self:GetLife0(), self:GetDamage()) if damaged then text=text.." Damaged!" end @@ -277,7 +298,7 @@ function TARGET:onafterStatus(From, Event, To) for i,_target in pairs(self.targets) do local target=_target --#TARGET.Object local damage=(1-target.Life/target.Life0)*100 - text=text..string.format("\n[%d] %s %s: Life=%.1f/%.1f, Damage=%.1f", i, target.Name, target.Status, target.Life, target.Life0, damage) + text=text..string.format("\n[%d] %s %s %s: Life=%.1f/%.1f, Damage=%.1f", i, target.Type, target.Name, target.Status, target.Life, target.Life0, damage) end self:I(self.lid..text) end @@ -397,7 +418,30 @@ function TARGET:_AddObject(Object) local target={} --#TARGET.Object - if Object:IsInstanceOf("UNIT") then + if Object:IsInstanceOf("GROUP") then + + local group=Object --Wrapper.Group#GROUP + + target.Type=TARGET.ObjectType.GROUP + target.Name=group:GetName() + + local units=group:GetUnits() + + target.Life=0 ; target.Life0=0 + for _,_unit in pairs(units or {}) do + local unit=_unit --Wrapper.Unit#UNIT + + local life=unit:GetLife() + + target.Life=target.Life+life + target.Life0=target.Life0+math.max(unit:GetLife0(), life) -- There was an issue with ships that life is greater life0, which cannot be! + + self.threatlevel0=self.threatlevel0+unit:GetThreatLevel() + + self.Ntargets0=self.Ntargets0+1 + end + + elseif Object:IsInstanceOf("UNIT") then local unit=Object --Wrapper.Unit#UNIT @@ -407,6 +451,10 @@ function TARGET:_AddObject(Object) if unit and unit:IsAlive() then target.Life=unit:GetLife() target.Life0=math.max(unit:GetLife0(), target.Life) -- There was an issue with ships that life is greater life0! + + self.threatlevel0=self.threatlevel0+unit:GetThreatLevel() + + self.Ntargets0=self.Ntargets0+1 end elseif Object:IsInstanceOf("STATIC") then @@ -418,7 +466,9 @@ function TARGET:_AddObject(Object) if static and static:IsAlive() then target.Life0=1 - target.Life=1 + target.Life=1 + + self.Ntargets0=self.Ntargets0+1 end @@ -430,8 +480,9 @@ function TARGET:_AddObject(Object) target.Name=airbase:GetName() target.Life0=1 - target.Life=1 - + target.Life=1 + + self.Ntargets0=self.Ntargets0+1 elseif Object:IsInstanceOf("COORDINATE") then @@ -442,6 +493,9 @@ function TARGET:_AddObject(Object) target.Life0=1 target.Life=1 + + -- TODO: does this make sense for a coordinate? + --self.Ntargets0=self.Ntargets0+1 else self:E(self.lid.."ERROR: Unknown object type!") @@ -488,7 +542,24 @@ end -- @return #number Life points of target. function TARGET:GetTargetLife(Target) - if Target.Type==TARGET.ObjectType.UNIT then + if Target.Type==TARGET.ObjectType.GROUP then + + if Target.Object and Target.Object:IsAlive() then + + local units=Target.Object:GetUnits() + + local life=0 + for _,_unit in pairs(units or {}) do + local unit=_unit --Wrapper.Unit#UNIT + life=life+unit:GetLife() + end + + return life + else + return 0 + end + + elseif Target.Type==TARGET.ObjectType.UNIT then if Target.Object and Target.Object:IsAlive() then return Target.Object:GetLife() @@ -526,19 +597,7 @@ end function TARGET:GetLife() local N=0 - - local function _GetLife(unit) - local unit=unit --Wrapper.Unit#UNIT - if Healthy then - local life=unit:GetLife() - local life0=unit:GetLife0() - - return math.max(life, life0) - else - return unit:GetLife() - end - end - + for _,_target in pairs(self.targets) do local Target=_target --#TARGET.Object @@ -549,6 +608,132 @@ function TARGET:GetLife() return N end + + +--- Get target coordinate. +-- @param #TARGET self +-- @param #TARGET.Object Target Target object. +-- @return Core.Point#COORDINATE Coordinate of the target. +function TARGET:GetTargetCoordinate(Target) + + if Target.Type==TARGET.ObjectType.GROUP then + + if Target.Object and Target.Object:IsAlive() then + + return Target.Object:GetCoordinate() + + end + + elseif Target.Type==TARGET.ObjectType.UNIT then + + if Target.Object and Target.Object:IsAlive() then + return Target.Object:GetCoordinate() + end + + elseif Target.Type==TARGET.ObjectType.STATIC then + + if Target.Object and Target.Object:IsAlive() then + return Target.Object:GetCoordinate() + end + + elseif Target.Type==TARGET.ObjectType.AIRBASE then + + if Target.Status==TARGET.ObjectStatus.ALIVE then + return Target.Object:GetCoordinate() + end + + elseif Target.Type==TARGET.ObjectType.COORDINATE then + + return Target.Object + + end + + return nil +end + +--- Get coordinate. +-- @param #TARGET self +-- @return Core.Point#COORDINATE Coordinate of the target. +function TARGET:GetCoordinate() + + for _,_target in pairs(self.targets) do + local Target=_target --#TARGET.Object + + local coordinate=self:GetTargetCoordinate(Target) + + if coordinate then + return coordinate + end + + end + + return nil +end + + +--- Get target category +-- @param #TARGET self +-- @param #TARGET.Object Target Target object. +-- @return #TARGET.Category Target category. +function TARGET:GetTargetCategory(Target) + + local category=nil + + if Target.Type==TARGET.ObjectType.GROUP then + + if Target.Object and Target.Object:IsAlive()~=nil then + + local group=Target.Object --Wrapper.Group#GROUP + + local cat=group:GetCategory() + + if cat==Group.Category.AIRPLANE or cat==Group.Category.HELICOPTER then + category=TARGET.Category.AIRCRAFT + elseif cat==Group.Category.GROUND or cat==Group.Category.TRAIN then + category=TARGET.Category.GROUND + elseif cat==Group.Category.SHIP then + category=TARGET.Category.NAVAL + end + + end + + elseif Target.Type==TARGET.ObjectType.UNIT then + + if Target.Object and Target.Object:IsAlive() then + local unit=Target.Object --Wrapper.Unit#UNIT + + local group=unit:GetGroup() + + local cat=group:GetCategory() + + if cat==Group.Category.AIRPLANE or cat==Group.Category.HELICOPTER then + category=TARGET.Category.AIRCRAFT + elseif cat==Group.Category.GROUND or cat==Group.Category.TRAIN then + category=TARGET.Category.GROUND + elseif cat==Group.Category.SHIP then + category=TARGET.Category.NAVAL + end + + end + + elseif Target.Type==TARGET.ObjectType.STATIC then + + return TARGET.Category.GROUND + + elseif Target.Type==TARGET.ObjectType.AIRBASE then + + return TARGET.Category.AIRBASE + + elseif Target.Type==TARGET.ObjectType.COORDINATE then + + return TARGET.Category.COORDINATE + + end + + return category +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -580,7 +765,20 @@ function TARGET:CountTargets() for _,_target in pairs(self.targets) do local Target=_target --#TARGET.Object - if Target.Type==TARGET.ObjectType.UNIT then + if Target.Type==TARGET.ObjectType.GROUP then + + local target=Target.Object --Wrapper.Group#GROUP + + local units=target:GetUnits() + + for _,_unit in pairs(units or {}) do + local unit=_unit --Wrapper.Unit#UNIT + if unit and unit:IsAlive() and unit:GetLife()>1 then + N=N+1 + end + end + + elseif Target.Type==TARGET.ObjectType.UNIT then local target=Target.Object --Wrapper.Unit#UNIT diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 36a7ca95e..ccd19ae60 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2213,6 +2213,45 @@ function GROUP:GetDCSDesc(n) return nil end +--- Get health of the group. +-- @param #GROUP self +-- @return #number Health in percent. +function GROUP:GetHealth() + + local lp=0 + local lp0=0 + + local units=self:GetUnits() + + for _,_unit in pairs(units or {}) do + local unit=_unit --Wrapper.Unit#UNIT + + if unit and unit:IsAlive() then + local life=unit:GetLife() + local life0=unit:GetLife0() + life0=math.max(life0, life) --Issue with ships + + lp=lp+life + lp0=lp0+life + + end + + end + + if lp0>0 then + return lp/lp0*100 + else + return 0 + end +end + +--- Get damage of the group. +-- @param #GROUP self +-- @return #number Damage in percent. +function GROUP:GetDamage() + return 100-self:GetHealth() +end + --- Get the generalized attribute of a self. -- Note that for a heterogenious self, the attribute is determined from the attribute of the first unit!