diff --git a/Moose Development/Moose/Core/Astar.lua b/Moose Development/Moose/Core/Astar.lua index 89cb7086b..9514d179e 100644 --- a/Moose Development/Moose/Core/Astar.lua +++ b/Moose Development/Moose/Core/Astar.lua @@ -10,6 +10,8 @@ -- === -- -- ### Author: **funkyfranky** +-- +-- === -- @module Core.Astar -- @image CORE_Astar.png @@ -36,12 +38,10 @@ -- @field #table CostArg Optional arguments passed to the cost function. -- @extends Core.Base#BASE ---- **When nothing goes right... Go left!** +--- *When nothing goes right... Go left!* -- -- === -- --- ![Banner Image](..\Presentations\Astar\ASTAR_Main.jpg) --- -- # The ASTAR Concept -- -- Pathfinding algorithm. diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 2826969bb..ae40ec4ca 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2164,14 +2164,21 @@ do -- COORDINATE if ReadOnly==nil then ReadOnly=false end + local vec3=self:GetVec3() + Radius=Radius or 1000 + Coalition=Coalition or -1 + Color=Color or {1,0,0} Color[4]=Alpha or 1.0 + LineType=LineType or 1 - FillColor=FillColor or Color + + FillColor=FillColor or UTILS.DeepCopy(Color) FillColor[4]=FillAlpha or 0.15 + trigger.action.circleToAll(Coalition, MarkID, vec3, Radius, Color, FillColor, LineType, ReadOnly, Text or "") return MarkID end @@ -2196,13 +2203,19 @@ do -- COORDINATE if ReadOnly==nil then ReadOnly=false end + local vec3=Endpoint:GetVec3() + Coalition=Coalition or -1 + Color=Color or {1,0,0} Color[4]=Alpha or 1.0 + LineType=LineType or 1 - FillColor=FillColor or Color + + FillColor=FillColor or UTILS.DeepCopy(Color) FillColor[4]=FillAlpha or 0.15 + trigger.action.rectToAll(Coalition, MarkID, self:GetVec3(), vec3, Color, FillColor, LineType, ReadOnly, Text or "") return MarkID end @@ -2226,17 +2239,23 @@ do -- COORDINATE if ReadOnly==nil then ReadOnly=false end + local point1=self:GetVec3() local point2=Coord2:GetVec3() local point3=Coord3:GetVec3() local point4=Coord4:GetVec3() + Coalition=Coalition or -1 + Color=Color or {1,0,0} Color[4]=Alpha or 1.0 + LineType=LineType or 1 - FillColor=FillColor or Color + + FillColor=FillColor or UTILS.DeepCopy(Color) FillColor[4]=FillAlpha or 0.15 - trigger.action.quadToAll(Coalition, MarkID, self:GetVec3(), point2, point3, point4, Color, FillColor, LineType, ReadOnly, Text or "") + + trigger.action.quadToAll(Coalition, MarkID, point1, point2, point3, point4, Color, FillColor, LineType, ReadOnly, Text or "") return MarkID end @@ -2320,11 +2339,15 @@ do -- COORDINATE ReadOnly=false end Coalition=Coalition or -1 + Color=Color or {1,0,0} Color[4]=Alpha or 1.0 - FillColor=FillColor or Color + + FillColor=FillColor or UTILS.DeepCopy(Color) FillColor[4]=FillAlpha or 0.3 + FontSize=FontSize or 14 + trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World") return MarkID end @@ -2346,13 +2369,19 @@ do -- COORDINATE if ReadOnly==nil then ReadOnly=false end + local vec3=Endpoint:GetVec3() + Coalition=Coalition or -1 + Color=Color or {1,0,0} Color[4]=Alpha or 1.0 + LineType=LineType or 1 - FillColor=FillColor or Color + + FillColor=FillColor or UTILS.DeepCopy(Color) FillColor[4]=FillAlpha or 0.15 + --trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World") trigger.action.arrowToAll(Coalition, MarkID, vec3, self:GetVec3(), Color, FillColor, LineType, ReadOnly, Text or "") return MarkID diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 85214622f..6b6bd46e1 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -622,7 +622,7 @@ function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, Lin Color=Color or self:GetColorRGB() Alpha=Alpha or 1 - FillColor=FillColor or Color + FillColor=FillColor or UTILS.DeepCopy(Color) FillAlpha=FillAlpha or self:GetColorAlpha() self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) @@ -1862,7 +1862,8 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph Color=Color or self:GetColorRGB() Alpha=Alpha or 1 - FillColor=FillColor or Color + + FillColor=FillColor or UTILS.DeepCopy(Color) FillAlpha=FillAlpha or self:GetColorAlpha() diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index c022ab0af..88b252ec4 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -23,7 +23,6 @@ -- ### Author: **funkyfranky** -- -- == --- -- @module Ops.ArmyGroup -- @image OPS_ArmyGroup.png @@ -38,7 +37,7 @@ -- @field Core.Set#SET_ZONE retreatZones Set of retreat zones. -- @extends Ops.OpsGroup#OPSGROUP ---- *Your soul may belong to Jesus, but your ass belongs to the marines* -- Eugene B Sledge +--- *Your soul may belong to Jesus, but your ass belongs to the marines.* -- Eugene B Sledge -- -- === -- diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index b1d1c90c4..e22a204a6 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -165,7 +165,7 @@ -- -- @extends Core.Fsm#FSM ---- *A warrior's mission is to foster the success of others.* - Morihei Ueshiba +--- *A warrior's mission is to foster the success of others.* -- Morihei Ueshiba -- -- === -- @@ -436,8 +436,9 @@ AUFTRAG.Type={ --- Special task description. -- @type AUFTRAG.SpecialTask +-- @field #string FORMATION AI formation task. -- @field #string PATROLZONE Patrol zone task. --- @field #string RECON Recon task +-- @field #string RECON Recon task. -- @field #string AMMOSUPPLY Ammo Supply. -- @field #string FUELSUPPLY Fuel Supply. -- @field #string ALERT5 Alert 5 task. @@ -450,6 +451,7 @@ AUFTRAG.Type={ -- @field #string NOTHING Nothing. -- @field #string RELOCATECOHORT Relocate cohort. AUFTRAG.SpecialTask={ + FORMATION="Formation", PATROLZONE="PatrolZone", RECON="ReconMission", AMMOSUPPLY="Ammo Supply", @@ -584,7 +586,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.9.2" +AUFTRAG.version="0.9.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -937,7 +939,7 @@ function AUFTRAG:NewANTISHIP(Target, Altitude) return mission end ---- **[AIR/HELICOPTER]** Create an HOVER mission. +--- **[AIR ROTARY]** Create an HOVER mission. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to hover. -- @param #number Altitude Hover altitude in feet AGL. Default is 50 feet above ground. @@ -968,7 +970,7 @@ function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense - mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.categories={AUFTRAG.Category.HELICOPTER} mission.DCStask=mission:GetDCSMissionTask() @@ -1257,8 +1259,8 @@ end -- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL. -- @param #number Speed Speed in knots. -- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM. --- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default `{"Helicopters", "Ground Units", "Light armed ships"}`. -- @param Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere. +-- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default `{"Helicopters", "Ground Units", "Light armed ships"}`. -- @return #AUFTRAG self function AUFTRAG:NewCASENHANCED(CasZone, Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes) @@ -4857,6 +4859,24 @@ function AUFTRAG:GetMissionEgressCoord() return self.missionEgressCoord end +--- Get coordinate which was set as mission waypoint coordinate. +-- @param #AUFTRAG self +-- @return Core.Point#COORDINATE Coordinate where the mission is executed or `#nil`. +function AUFTRAG:_GetMissionWaypointCoordSet() + + -- Check if a coord has been explicitly set. + if self.missionWaypointCoord then + local coord=self.missionWaypointCoord + if self.missionAltitude then + coord.y=self.missionAltitude + end + + + return coord + end + +end + --- Get coordinate of target. First unit/group of the set is used. -- @param #AUFTRAG self -- @param Wrapper.Group#GROUP group Group. @@ -5102,7 +5122,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) local DCStask={} - DCStask.id="ReconMission" + DCStask.id=AUFTRAG.SpecialTask.RECON -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} @@ -5202,7 +5222,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) local DCStask={} - DCStask.id="Formation" + DCStask.id=AUFTRAG.SpecialTask.FORMATION -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} @@ -5222,9 +5242,30 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- ARTY Mission -- ------------------ - local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, self:GetTargetVec2(), self.artyRadius, self.artyShots, self.engageWeaponType, self.artyAltitude) + + if self.artyShots==1 or self.artyRadius<10 or true then + + local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, self:GetTargetVec2(), self.artyRadius, self.artyShots, self.engageWeaponType, self.artyAltitude) + table.insert(DCStasks, DCStask) + + else + + local Vec2=self:GetTargetVec2() + + local zone=ZONE_RADIUS:New("temp", Vec2, self.artyRadius) + + for i=1,self.artyShots do + + local vec2=zone:GetRandomVec2() - table.insert(DCStasks, DCStask) + local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, vec2, 0, 1, self.engageWeaponType, self.artyAltitude) + table.insert(DCStasks, DCStask) + + end + + end + + --table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.BARRAGE then @@ -5258,7 +5299,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) local DCStask={} - DCStask.id="PatrolZone" + DCStask.id=AUFTRAG.SpecialTask.PATROLZONE -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} @@ -5278,7 +5319,7 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) local DCStask={} - DCStask.id="PatrolZone" + DCStask.id=AUFTRAG.SpecialTask.PATROLZONE -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index d261e3a30..50130d0a2 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -118,7 +118,7 @@ -- -- ## Adding Fleets -- --- Fleets are not implemented yet. +-- Fleets can be added via the @{#CHIEF.AddFleet}() function. -- -- -- # Strategic (Capture) Zones @@ -866,12 +866,24 @@ end -- @return #CHIEF self function CHIEF:AddBrigade(Brigade) - -- Add brigade to the commander + -- Add brigade to the commander. self:AddLegion(Brigade) return self end +--- Add a FLEET to the chief's commander. +-- @param #CHIEF self +-- @param Ops.Fleet#FLEET Fleet The fleet to add. +-- @return #CHIEF self +function CHIEF:AddFleet(Fleet) + + -- Add fleet to the commander. + self:AddLegion(Fleet) + + return self +end + --- Add a LEGION to the chief's commander. -- @param #CHIEF self -- @param Ops.Legion#LEGION Legion The legion to add. @@ -2373,7 +2385,7 @@ end --- Get mission performance for a given TARGET. -- @param #CHIEF self --- @param Ops.Target#TARGET Target +-- @param Ops.Target#TARGET Target The target. -- @return #table Mission performances of type `#CHIEF.MissionPerformance`. function CHIEF:_GetMissionPerformanceFromTarget(Target) diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index 486f7dec1..a41fc240e 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -47,7 +47,7 @@ -- @field #number cargobayLimit Cargo bay capacity in kg. -- @extends Core.Fsm#FSM ---- *It is unbelievable what a platoon of twelve aircraft did to tip the balance* -- Adolf Galland +--- *I came, I saw, I conquered.* -- Julius Caesar -- -- === -- @@ -89,7 +89,7 @@ COHORT.version="0.3.0" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Create FLOTILLA class. +-- DONE: Create FLOTILLA class. -- DONE: Added check for properties. -- DONE: Make general so that PLATOON and SQUADRON can inherit this class. @@ -613,6 +613,49 @@ function COHORT:ReturnTacan(channel) self.tacanChannel[channel]=true end +--- Add a weapon range for ARTY missions (@{Ops.Auftrag#AUFTRAG}). +-- @param #COHORT self +-- @param #number RangeMin Minimum range in nautical miles. Default 0 NM. +-- @param #number RangeMax Maximum range in nautical miles. Default 10 NM. +-- @param #number BitType Bit mask of weapon type for which the given min/max ranges apply. Default is `ENUMS.WeaponFlag.Auto`, i.e. for all weapon types. +-- @return #COHORT self +function COHORT:AddWeaponRange(RangeMin, RangeMax, BitType) + + RangeMin=UTILS.NMToMeters(RangeMin or 0) + RangeMax=UTILS.NMToMeters(RangeMax or 10) + + local weapon={} --Ops.OpsGroup#OPSGROUP.WeaponData + + weapon.BitType=BitType or ENUMS.WeaponFlag.Auto + weapon.RangeMax=RangeMax + weapon.RangeMin=RangeMin + + self.weaponData=self.weaponData or {} + self.weaponData[tostring(weapon.BitType)]=weapon + + -- Debug info. + self:T(self.lid..string.format("Adding weapon data: Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapon.BitType), weapon.RangeMin, weapon.RangeMax)) + + if self.verbose>=2 then + local text="Weapon data:" + for _,_weapondata in pairs(self.weaponData) do + local weapondata=_weapondata + text=text..string.format("\n- Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapondata.BitType), weapondata.RangeMin, weapondata.RangeMax) + end + self:I(self.lid..text) + end + + return self +end + +--- Get weapon range for given bit type. +-- @param #COHORT self +-- @param #number BitType Bit mask of weapon type. +-- @return Ops.OpsGroup#OPSGROUP.WeaponData Weapon data. +function COHORT:GetWeaponData(BitType) + return self.weaponData[tostring(BitType)] +end + --- Check if cohort is "OnDuty". -- @param #COHORT self -- @return #boolean If true, cohort is in state "OnDuty". @@ -1048,6 +1091,42 @@ function COHORT:GetRepairTime(Asset) end +--- Get max mission range. We add the largest weapon range, e.g. for arty or naval if weapon data is available. +-- @param #COHORT self +-- @param #table WeaponTypes (Optional) Weapon bit type(s) to add to the total range. Default is the max weapon type available. +-- @return #number Range in meters. +function COHORT:GetMissionRange(WeaponTypes) + + if WeaponTypes and type(WeaponTypes)~="table" then + WeaponTypes={WeaponTypes} + end + + local function checkWeaponType(Weapon) + local weapon=Weapon --Ops.OpsGroup#OPSGROUP.WeaponData + if WeaponTypes and #WeaponTypes>0 then + for _,weapontype in pairs(WeaponTypes) do + if weapontype==weapon.BitType then + return true + end + end + return false + end + return true + end + + -- Get max weapon range. + local WeaponRange=0 + for _,_weapon in pairs(self.weaponData or {}) do + local weapon=_weapon --Ops.OpsGroup#OPSGROUP.WeaponData + + if weapon.RangeMax>WeaponRange and checkWeaponType(weapon) then + WeaponRange=weapon.RangeMax + end + end + + return self.engageRange+WeaponRange +end + --- Checks if a mission type is contained in a table of possible types. -- @param #COHORT self -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset. @@ -1088,6 +1167,237 @@ function COHORT:CheckAttribute(Attributes) return false end +--- Check ammo. +-- @param #COHORT self +-- @return Ops.OpsGroup#OPSGROUP.Ammo Ammo. +function COHORT:_CheckAmmo() + + -- Get units of group. + local units=self.templategroup:GetUnits() + + -- Init counter. + local nammo=0 + local nguns=0 + local nshells=0 + local nrockets=0 + local nmissiles=0 + local nmissilesAA=0 + local nmissilesAG=0 + local nmissilesAS=0 + local nmissilesSA=0 + local nmissilesBM=0 + local nmissilesCR=0 + local ntorps=0 + local nbombs=0 + + + for _,_unit in pairs(units) do + local unit=_unit --Wrapper.Unit#UNIT + + -- Output. + local text=string.format("Unit %s:\n", unit:GetName()) + + -- Get ammo table. + local ammotable=unit:GetAmmo() + + if ammotable then + + -- Debug info. + self:T3(ammotable) + + -- Loop over all weapons. + for w=1,#ammotable do + + -- Weapon table. + local weapon=ammotable[w] + + -- Descriptors. + local Desc=weapon["desc"] + + -- Warhead. + local Warhead=Desc["warhead"] + + -- Number of current weapon. + local Nammo=weapon["count"] + + -- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3, torpedo=4 + local Category=Desc["category"] + + -- Get missile category: Weapon.MissileCategory AAM=1, SAM=2, BM=3, ANTI_SHIP=4, CRUISE=5, OTHER=6 + local MissileCategory = (Category==Weapon.Category.MISSILE) and Desc.missileCategory or nil + + -- Type name of current weapon. + local TypeName=Desc["typeName"] + + -- WeaponName + local weaponString = UTILS.Split(TypeName,"%.") + local WeaponName = weaponString[#weaponString] + + + -- Range in meters. Seems only to exist for missiles (not shells). + local Rmin=Desc["rangeMin"] or 0 + local Rmax=Desc["rangeMaxAltMin"] or 0 + + -- Caliber in mm. + local Caliber=Warhead and Warhead["caliber"] or 0 + + + -- We are specifically looking for shells or rockets here. + if Category==Weapon.Category.SHELL then + --- + -- SHELL + --- + + -- Add up all shells. + if Caliber<70 then + nguns=nguns+Nammo + else + nshells=nshells+Nammo + end + + -- Debug info. + text=text..string.format("- %d shells [%s]: caliber=%d mm, range=%d - %d meters\n", Nammo, WeaponName, Caliber, Rmin, Rmax) + + elseif Category==Weapon.Category.ROCKET then + --- + -- ROCKET + --- + + -- Add up all rockets. + nrockets=nrockets+Nammo + + -- Debug info. + text=text..string.format("- %d rockets [%s]: caliber=%d mm, range=%d - %d meters\n", Nammo, WeaponName, Caliber, Rmin, Rmax) + + elseif Category==Weapon.Category.BOMB then + --- + -- BOMB + --- + + -- Add up all rockets. + nbombs=nbombs+Nammo + + -- Debug info. + text=text..string.format("- %d bombs [%s]: caliber=%d mm, range=%d - %d meters\n", Nammo, WeaponName, Caliber, Rmin, Rmax) + + elseif Category==Weapon.Category.MISSILE then + --- + -- MISSILE + --- + + -- Add up all cruise missiles (category 5) + if MissileCategory==Weapon.MissileCategory.AAM then + nmissiles=nmissiles+Nammo + nmissilesAA=nmissilesAA+Nammo + -- Auto add range for AA missles. Useless here as this is not an aircraft. + if Rmax>0 then + self:AddWeaponRange(UTILS.MetersToNM(Rmin), UTILS.MetersToNM(Rmax), ENUMS.WeaponFlag.AnyAA) + end + elseif MissileCategory==Weapon.MissileCategory.SAM then + nmissiles=nmissiles+Nammo + nmissilesSA=nmissilesSA+Nammo + -- Dont think there is a bit type for SAM. + if Rmax>0 then + --self:AddWeaponRange(Rmin, Rmax, ENUMS.WeaponFlag.AnyASM) + end + elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then + nmissiles=nmissiles+Nammo + nmissilesAS=nmissilesAS+Nammo + -- Auto add weapon range for anti-ship missile. + if Rmax>0 then + self:AddWeaponRange(UTILS.MetersToNM(Rmin), UTILS.MetersToNM(Rmax), ENUMS.WeaponFlag.AntiShipMissile) + end + elseif MissileCategory==Weapon.MissileCategory.BM then + nmissiles=nmissiles+Nammo + nmissilesBM=nmissilesBM+Nammo + -- Don't think there is a good bit type for ballistic missiles. + if Rmax>0 then + --self:AddWeaponRange(Rmin, Rmax, ENUMS.WeaponFlag.AnyASM) + end + elseif MissileCategory==Weapon.MissileCategory.CRUISE then + nmissiles=nmissiles+Nammo + nmissilesCR=nmissilesCR+Nammo + -- Auto add weapon range for cruise missile. + if Rmax>0 then + self:AddWeaponRange(UTILS.MetersToNM(Rmin), UTILS.MetersToNM(Rmax), ENUMS.WeaponFlag.CruiseMissile) + end + elseif MissileCategory==Weapon.MissileCategory.OTHER then + nmissiles=nmissiles+Nammo + nmissilesAG=nmissilesAG+Nammo + end + + -- Debug info. + text=text..string.format("- %d %s missiles [%s]: caliber=%d mm, range=%d - %d meters\n", Nammo, self:_MissileCategoryName(MissileCategory), WeaponName, Caliber, Rmin, Rmax) + + elseif Category==Weapon.Category.TORPEDO then + + -- Add up all rockets. + ntorps=ntorps+Nammo + + -- Debug info. + text=text..string.format("- %d torpedos [%s]: caliber=%d mm, range=%d - %d meters\n", Nammo, WeaponName, Caliber, Rmin, Rmax) + + else + + -- Debug info. + text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n", Nammo, TypeName, Category, tostring(MissileCategory)) + + end + + end + end + + -- Debug text and send message. + if self.verbose>=5 then + self:I(self.lid..text) + else + self:T2(self.lid..text) + end + + end + + -- Total amount of ammunition. + nammo=nguns+nshells+nrockets+nmissiles+nbombs+ntorps + + local ammo={} --Ops.OpsGroup#OPSGROUP.Ammo + ammo.Total=nammo + ammo.Guns=nguns + ammo.Shells=nshells + ammo.Rockets=nrockets + ammo.Bombs=nbombs + ammo.Torpedos=ntorps + ammo.Missiles=nmissiles + ammo.MissilesAA=nmissilesAA + ammo.MissilesAG=nmissilesAG + ammo.MissilesAS=nmissilesAS + ammo.MissilesCR=nmissilesCR + ammo.MissilesBM=nmissilesBM + ammo.MissilesSA=nmissilesSA + + return ammo +end + +--- Returns a name of a missile category. +-- @param #COHORT self +-- @param #number categorynumber Number of missile category from weapon missile category enumerator. See https://wiki.hoggitworld.com/view/DCS_Class_Weapon +-- @return #string Missile category name. +function COHORT:_MissileCategoryName(categorynumber) + local cat="unknown" + if categorynumber==Weapon.MissileCategory.AAM then + cat="air-to-air" + elseif categorynumber==Weapon.MissileCategory.SAM then + cat="surface-to-air" + elseif categorynumber==Weapon.MissileCategory.BM then + cat="ballistic" + elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then + cat="anti-ship" + elseif categorynumber==Weapon.MissileCategory.CRUISE then + cat="cruise" + elseif categorynumber==Weapon.MissileCategory.OTHER then + cat="other" + end + return cat +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 95c545dfe..3aeabeb10 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -8,6 +8,8 @@ -- === -- -- ### Author: **funkyfranky** +-- +-- === -- @module Ops.Commander -- @image OPS_Commander.png @@ -32,7 +34,7 @@ -- @field #table limitMission Table of limits for mission types. -- @extends Core.Fsm#FSM ---- *He who has never leared to obey cannot be a good commander* -- Aristotle +--- *He who has never leared to obey cannot be a good commander.* -- Aristotle -- -- === -- diff --git a/Moose Development/Moose/Ops/Fleet.lua b/Moose Development/Moose/Ops/Fleet.lua index e07d0d807..515e5cf14 100644 --- a/Moose Development/Moose/Ops/Fleet.lua +++ b/Moose Development/Moose/Ops/Fleet.lua @@ -25,23 +25,44 @@ -- @field #string ClassName Name of the class. -- @field #number verbose Verbosity of output. -- @field Core.Set#SET_ZONE retreatZones Retreat zone set. +-- @field #boolean pathfinding Set pathfinding on for all spawned navy groups. -- @extends Ops.Legion#LEGION ---- *I am not afraid of an Army of lions lead by a sheep; I am afraid of sheep lead by a lion* -- Alexander the Great +--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson -- -- === -- -- # The FLEET Concept -- -- A FLEET consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed. +-- +-- # Basic Setup +-- +-- A new `FLEET` object can be created with the @{#FLEET.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet +-- and `FleetName` is the name you want to give the fleet. This must be *unique*! +-- +-- myFleet=FLEET:New("myWarehouseName", "1st Fleet") +-- myFleet:SetPortZone(ZonePort1stFleet) +-- myFleet:Start() +-- +-- A fleet needs a *port zone*, which is set via the @{#FLEET.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to. +-- +-- Finally, the fleet needs to be started using the @{#FLEET.Start}() function. If the fleet is not started, it will not process any requests. +-- +-- ## Adding Flotillas +-- +-- Flotillas can be added via the @{#FLEET.AddFlotilla}(`Flotilla`) function. See @{Ops.Flotilla#FLOTILLA} for how to create a flotilla. +-- +-- myFleet:AddFlotilla(FlotillaTiconderoga) +-- myFleet:AddFlotilla(FlotillaPerry) +-- -- -- -- @field #FLEET FLEET = { ClassName = "FLEET", - verbose = 0, - rearmingZones = {}, - refuellingZones = {}, + verbose = 0, + pathfinding = false, } --- Supply Zone. @@ -59,7 +80,7 @@ FLEET.version="0.0.1" -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: . +-- TODO: Add routes? -- DONE: Add weapon range. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -199,6 +220,15 @@ function FLEET:AddAssetToFlotilla(Flotilla, Nassets) return self end +--- Set pathfinding for all spawned naval groups. +-- @param #FLEET self +-- @param #boolean Switch If `true`, pathfinding is used. +-- @return #FLEET self +function FLEET:SetPathfinding(Switch) + self.pathfinding=Switch + return self +end + --- Define a set of retreat zones. -- @param #FLEET self -- @param Core.Set#SET_ZONE RetreatZoneSet Set of retreat zones. diff --git a/Moose Development/Moose/Ops/Flotilla.lua b/Moose Development/Moose/Ops/Flotilla.lua index 44c41606c..7711f9b60 100644 --- a/Moose Development/Moose/Ops/Flotilla.lua +++ b/Moose Development/Moose/Ops/Flotilla.lua @@ -22,13 +22,13 @@ -- @field Ops.OpsGroup#OPSGROUP.WeaponData weaponData Weapon data table with key=BitType. -- @extends Ops.Cohort#COHORT ---- *Some cool cohort quote* -- Known Author +--- *No captain can do very wrong if he places his ship alongside that of an enemy.* -- Horation Nelson -- -- === -- -- # The FLOTILLA Concept -- --- A FLOTILLA is essential part of a FLEET. +-- A FLOTILLA is an essential part of a FLEET. -- -- -- @@ -64,6 +64,7 @@ function FLOTILLA:New(TemplateGroupName, Ngroups, FlotillaName) -- Inherit everything from COHORT class. local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, FlotillaName)) -- #FLOTILLA + self.ammo=self:_CheckAmmo() return self end @@ -90,41 +91,6 @@ function FLOTILLA:GetFleet() return self.legion end ---- Add a weapon range for ARTY auftrag. --- @param #FLOTILLA self --- @param #number RangeMin Minimum range in nautical miles. Default 0 NM. --- @param #number RangeMax Maximum range in nautical miles. Default 10 NM. --- @param #number BitType Bit mask of weapon type for which the given min/max ranges apply. Default is `ENUMS.WeaponFlag.Auto`, i.e. for all weapon types. --- @return #FLOTILLA self -function FLOTILLA:AddWeaponRange(RangeMin, RangeMax, BitType) - - RangeMin=UTILS.NMToMeters(RangeMin or 0) - RangeMax=UTILS.NMToMeters(RangeMax or 10) - - local weapon={} --Ops.OpsGroup#OPSGROUP.WeaponData - - weapon.BitType=BitType or ENUMS.WeaponFlag.Auto - weapon.RangeMax=RangeMax - weapon.RangeMin=RangeMin - - self.weaponData=self.weaponData or {} - self.weaponData[tostring(weapon.BitType)]=weapon - - -- Debug info. - self:T(self.lid..string.format("Adding weapon data: Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapon.BitType), weapon.RangeMin, weapon.RangeMax)) - - if self.verbose>=2 then - local text="Weapon data:" - for _,_weapondata in pairs(self.weaponData) do - local weapondata=_weapondata - text=text..string.format("\n- Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapondata.BitType), weapondata.RangeMin, weapondata.RangeMax) - end - self:I(self.lid..text) - end - - return self -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -157,7 +123,6 @@ function FLOTILLA:onafterStatus(From, Event, To) local fsmstate=self:GetState() local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A" - local modex=self.modex and self.modex or -1 local skill=self.skill and tostring(self.skill) or "N/A" local NassetsTot=#self.assets @@ -168,8 +133,8 @@ function FLOTILLA:onafterStatus(From, Event, To) end -- Short info. - local text=string.format("%s [Type=%s, Call=%s, Modex=%d, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]", - fsmstate, self.aircrafttype, callsign, modex, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ) + local text=string.format("%s [Type=%s, Call=%s, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]", + fsmstate, self.aircrafttype, callsign, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ) self:T(self.lid..text) -- Weapon data info. @@ -196,7 +161,7 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Misc functions. + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 5cae17ab1..bb4838d3a 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -23,7 +23,7 @@ -- @field Ops.Chief#CHIEF chief Chief of this legion. -- @extends Functional.Warehouse#WAREHOUSE ---- *Per aspera ad astra* +--- *Per aspera ad astra.* -- -- === -- @@ -47,13 +47,13 @@ LEGION = { --- LEGION class version. -- @field #string version -LEGION.version="0.3.0" +LEGION.version="0.3.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Create FLEED class. +-- DONE: Create FLEED class. -- DONE: Relocate cohorts. -- DONE: Aircraft will not start hot on Alert5. -- DONE: OPS transport. @@ -1135,7 +1135,8 @@ function LEGION:onafterOpsOnMission(From, Event, To, OpsGroup, Mission) -- Trigger event for Brigades. self:ArmyOnMission(OpsGroup, Mission) else - --TODO: Flotilla + -- Trigger event for Fleets. + self:NavyOnMission(OpsGroup, Mission) end -- Trigger event for chief. @@ -1363,6 +1364,11 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) -- Assignment. local assignment=request.assignment + -- Set pathfinding for naval groups. + if self:IsFleet() then + flightgroup:SetPathfinding(self.pathfinding) + end + if string.find(assignment, "Mission-") then --- @@ -2029,7 +2035,8 @@ function LEGION:RecruitAssetsForMission(Mission) end -- Recuit assets. - local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem) + local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, + Mission.engageRange, Mission.refuelSystem, nil, nil, nil, nil, nil, {Mission.engageWeaponType}) return recruited, assets, legions end @@ -2142,10 +2149,11 @@ end -- @param #table Categories Group categories. -- @param #table Attributes Group attributes. See `GROUP.Attribute.` -- @param #table Properties DCS attributes. +-- @param #table WeaponTypes Bit of weapon types. -- @return #boolean If `true` enough assets could be recruited. -- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else. -- @return #table Legions of recruited assets. -function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes, Properties) +function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes, Properties, WeaponTypes) -- The recruited assets. local Assets={} @@ -2202,8 +2210,23 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, end end - --BASE:I({Attributes=Attributes}) - --BASE:I({Properties=Properties}) + --- Function to check weapon type. + local function CheckWeapon(_cohort) + local cohort=_cohort --Ops.Cohort#COHORT + if WeaponTypes and #WeaponTypes>0 then + for _,WeaponType in pairs(WeaponTypes) do + for _,_weaponData in pairs(cohort.weaponData or {}) do + local weaponData=_weaponData --Ops.OpsGroup#OPSGROUP.WeaponData + if weaponData.BitType==WeaponType then + return true + end + end + end + return false + else + return true + end + end -- Loops over cohorts. for _,_cohort in pairs(Cohorts) do @@ -2213,7 +2236,8 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, local TargetDistance=TargetVec2 and UTILS.VecDist2D(TargetVec2, cohort.legion:GetVec2()) or 0 -- Is in range? - local InRange=(RangeMax and math.max(RangeMax, cohort.engageRange) or cohort.engageRange) >= TargetDistance + local Rmax=cohort:GetMissionRange(WeaponTypes) + local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance -- Has the requested refuelsystem? local Refuel=RefuelSystem~=nil and (RefuelSystem==cohort.tankerSystem) or true @@ -2245,12 +2269,15 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, -- Right property (DCS attribute). local RightProperty=CheckProperty(cohort) + -- Right weapon type. + local RightWeapon=CheckWeapon(cohort) + -- Debug info. - cohort:T2(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s, RightProperty=%s", - cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty))) + cohort:T2(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, Category=%s, Attribute=%s, Property=%s, Weapon=%s", + cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty), tostring(RightWeapon))) -- Check OnDuty, capable, in range and refueling type (if TANKER). - if cohort:IsOnDuty() and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty then + if cohort:IsOnDuty() and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty and RightWeapon then -- Recruit assets from cohort. local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 0e8dfd4b3..f7caccd32 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -1496,16 +1496,56 @@ function NAVYGROUP:onafterDisengage(From, Event, To) -- Restore previous ROE and alarm state. self:SwitchROE(self.engage.roe) self:SwitchAlarmstate(self.engage.alarmstate) + + -- Get current task + local task=self:GetTaskCurrent() + + -- Get if current task is ground attack. + if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then + self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!") + self:TaskDone(task) + end -- Remove current waypoint if self.engage.Waypoint then - self:RemoveWaypointByID(self.engage.Waypoint.uid) + self:RemoveWaypointByID(self.engage.Waypoint.uid) end -- Check group is done self:_CheckGroupDone(1) end +--- On after "OutOfAmmo" event. +-- @param #NAVYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function NAVYGROUP:onafterOutOfAmmo(From, Event, To) + self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime())) + + -- Check if we want to retreat once out of ammo. + if self.retreatOnOutOfAmmo then + self:__Retreat(-1) + return + end + + -- Third, check if we want to RTZ once out of ammo. + if self.rtzOnOutOfAmmo then + self:__RTZ(-1) + end + + -- Get current task. + local task=self:GetTaskCurrent() + + if task then + if task.dcstask.id=="FireAtPoint" or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then + self:T(self.lid..string.format("Cancelling current %s task because out of ammo!", task.dcstask.id)) + self:TaskCancel(task) + end + end + +end + --- On after "RTZ" event. -- @param #NAVYGROUP self -- @param #string From From state. @@ -1722,18 +1762,12 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx) offsetY=5.01 end - -- Current coordinate. - --local coordinate=self:GetCoordinate():SetAltitude(offsetY, true) - local vec3=self:GetVec3() vec3.y=offsetY -- Current heading. local heading=self:GetHeading() - -- Check from 500 meters in front. - --coordinate=coordinate:Translate(500, heading, true) - local function LoS(dist) local checkvec3=UTILS.VecTranslate(vec3, dist, heading) local los=land.isVisible(vec3, checkvec3) @@ -1780,8 +1814,9 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx) return 0 end + local _check=check() - return check() + return _check end --- Check if group is turning. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index c714332a6..06f7acab9 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -136,7 +136,7 @@ -- -- @extends Core.Fsm#FSM ---- *A small group of determined and like-minded people can change the course of history* -- Mahatma Gandhi +--- *A small group of determined and like-minded people can change the course of history.* -- Mahatma Gandhi -- -- === -- @@ -3671,7 +3671,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- Get mission of this task (if any). local Mission=self:GetMissionByTaskID(self.taskcurrent) - if Task.dcstask.id=="Formation" then + if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then -- Set of group(s) to follow Mother. local followSet=SET_GROUP:New():AddGroup(self.group) @@ -3681,7 +3681,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) local followUnit=UNIT:FindByName(param.unitname) -- Define AI Formation object. - Task.formation=AI_FORMATION:New(followUnit, followSet, "Formation", "Follow X at given parameters.") + Task.formation=AI_FORMATION:New(followUnit, followSet, AUFTRAG.SpecialTask.FORMATION, "Follow X at given parameters.") -- Formation parameters. Task.formation:FormationCenterWing(-param.offsetX, 50, math.abs(param.altitude), 50, param.offsetZ, 50) @@ -3695,7 +3695,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- Start formation FSM. Task.formation:Start() - elseif Task.dcstask.id=="PatrolZone" then + elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then --- -- Task patrol zone. @@ -3735,7 +3735,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- Set mission UID. wp.missionUID=Mission and Mission.auftragsnummer or nil - elseif Task.dcstask.id=="ReconMission" then + elseif Task.dcstask.id==AUFTRAG.SpecialTask.RECON then --- -- Task recon. @@ -3999,12 +3999,12 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) Task.stopflag:Set(1) local done=false - if Task.dcstask.id=="Formation" then + if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then Task.formation:Stop() done=true - elseif Task.dcstask.id=="PatrolZone" then + elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then done=true - elseif Task.dcstask.id=="ReconMission" then + elseif Task.dcstask.id==AUFTRAG.SpecialTask.RECON then done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.AMMOSUPPLY then done=true @@ -4885,19 +4885,23 @@ function OPSGROUP:RouteToMission(mission, delay) -- ARTY --- + -- Coord + local coord=waypointcoord + -- Get weapon range. local weapondata=self:GetWeaponData(mission.engageWeaponType) + local coordInRange=nil --Core.Point#COORDINATE if weapondata then -- Get target coordinate. local targetcoord=mission:GetTargetCoordinate() -- Heading to target. - local heading=self:GetCoordinate():HeadingTo(targetcoord) + local heading=coord:HeadingTo(targetcoord) -- Distance to target. - local dist=self:GetCoordinate():Get2DDistance(targetcoord) + local dist=coord:Get2DDistance(targetcoord) -- Check if we are within range. if dist>weapondata.RangeMax then @@ -4905,7 +4909,7 @@ function OPSGROUP:RouteToMission(mission, delay) local d=(dist-weapondata.RangeMax)*1.1 -- New waypoint coord. - waypointcoord=self:GetCoordinate():Translate(d, heading) + coordInRange=coord:Translate(d, heading) -- Debug info. self:T(self.lid..string.format("Out of max range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(mission.engageWeaponType))) @@ -4914,7 +4918,7 @@ function OPSGROUP:RouteToMission(mission, delay) local d=(dist-weapondata.RangeMin)*1.1 -- New waypoint coord. - waypointcoord=self:GetCoordinate():Translate(d, heading) + coordInRange=coord:Translate(d, heading) -- Debug info. self:T(self.lid..string.format("Out of min range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(mission.engageWeaponType))) @@ -4923,8 +4927,28 @@ function OPSGROUP:RouteToMission(mission, delay) else self:T(self.lid..string.format("No weapon data for weapon type %s", tostring(mission.engageWeaponType))) end - end + + if coordInRange then + -- Add waypoint at + local waypoint=nil --#OPSGROUP.Waypoint + if self:IsFlightgroup() then + waypoint=FLIGHTGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false) + elseif self:IsArmygroup() then + waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, mission.optionFormation, false) + elseif self:IsNavygroup() then + waypoint=NAVYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false) + end + waypoint.missionUID=mission.auftragsnummer + + -- Set waypoint coord to be the one in range. Take care of proper waypoint uid. + waypointcoord=coordInRange + uid=waypoint.uid + + end + + end + -- Add waypoint. local waypoint=nil --#OPSGROUP.Waypoint if self:IsFlightgroup() then @@ -5155,7 +5179,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint) mission=self:GetMissionByTaskID(task.id) end - if task and task.dcstask.id=="PatrolZone" then + if task and task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then --- -- SPECIAL TASK: Patrol Zone @@ -5194,7 +5218,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint) end wp.missionUID=mission and mission.auftragsnummer or nil - elseif task and task.dcstask.id=="ReconMission" then + elseif task and task.dcstask.id==AUFTRAG.SpecialTask.RECON then --- -- SPECIAL TASK: Recon Mission @@ -5216,8 +5240,9 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint) local n=1 if task.dcstask.params.randomly then n=UTILS.GetRandomTableElement(self.reconindecies) - else - table.remove(self.reconindecies, n) + else + n=self.reconindecies[1] + table.remove(self.reconindecies, 1) end -- Zone object. @@ -9160,8 +9185,10 @@ function OPSGROUP:_CheckStuck() -- Debug warning. self:T(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) - -- Give cruise command again. - if self:IsReturning() then + -- Check what is happening. + if self:IsEngaging() then + self:__Disengage(1) + elseif self:IsReturning() then self:__RTZ(1) else self:__Cruise(1) @@ -9173,7 +9200,6 @@ function OPSGROUP:_CheckStuck() self:T(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) --TODO: Stuck event! - -- Look for a current mission and cancel it as we do not seem to be able to perform it. local mission=self:GetMissionCurrent() if mission then @@ -11396,6 +11422,8 @@ function OPSGROUP:GetAmmoUnit(unit, display) if display==nil then display=false end + + unit=unit or self.group:GetUnit(1) -- Init counter. local nammo=0 @@ -11420,12 +11448,18 @@ function OPSGROUP:GetAmmoUnit(unit, display) if ammotable then local weapons=#ammotable + + --self:I(ammotable) -- Loop over all weapons. for w=1,weapons do -- Number of current weapon. local Nammo=ammotable[w]["count"] + + -- Range in meters. Seems only to exist for missiles (not shells). + local rmin=ammotable[w]["desc"]["rangeMin"] or 0 + local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0 -- Type name of current weapon. local Tammo=ammotable[w]["desc"]["typeName"] @@ -11449,7 +11483,7 @@ function OPSGROUP:GetAmmoUnit(unit, display) nshells=nshells+Nammo -- Debug info. - text=text..string.format("- %d shells of type %s\n", Nammo, _weaponName) + text=text..string.format("- %d shells of type %s, range=%d - %d meters\n", Nammo, _weaponName, rmin, rmax) elseif Category==Weapon.Category.ROCKET then @@ -11457,7 +11491,7 @@ function OPSGROUP:GetAmmoUnit(unit, display) nrockets=nrockets+Nammo -- Debug info. - text=text..string.format("- %d rockets of type %s\n", Nammo, _weaponName) + text=text..string.format("- %d rockets of type %s, \n", Nammo, _weaponName, rmin, rmax) elseif Category==Weapon.Category.BOMB then @@ -11491,7 +11525,7 @@ function OPSGROUP:GetAmmoUnit(unit, display) end -- Debug info. - text=text..string.format("- %d %s missiles of type %s\n", Nammo, self:_MissileCategoryName(MissileCategory), _weaponName) + text=text..string.format("- %d %s missiles of type %s, range=%d - %d meters\n", Nammo, self:_MissileCategoryName(MissileCategory), _weaponName, rmin, rmax) elseif Category==Weapon.Category.TORPEDO then diff --git a/Moose Development/Moose/Ops/Platoon.lua b/Moose Development/Moose/Ops/Platoon.lua index f5479a62f..100095cc2 100644 --- a/Moose Development/Moose/Ops/Platoon.lua +++ b/Moose Development/Moose/Ops/Platoon.lua @@ -63,6 +63,8 @@ function PLATOON:New(TemplateGroupName, Ngroups, PlatoonName) -- Inherit everything from COHORT class. local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, PlatoonName)) -- #PLATOON + -- Get ammo. + self.ammo=self:_CheckAmmo() return self end @@ -88,42 +90,6 @@ end function PLATOON:GetBrigade() return self.legion end - ---- Add a weapon range for ARTY auftrag. --- @param #PLATOON self --- @param #number RangeMin Minimum range in nautical miles. Default 0 NM. --- @param #number RangeMax Maximum range in nautical miles. Default 10 NM. --- @param #number BitType Bit mask of weapon type for which the given min/max ranges apply. Default is `ENUMS.WeaponFlag.Auto`, i.e. for all weapon types. --- @return #PLATOON self -function PLATOON:AddWeaponRange(RangeMin, RangeMax, BitType) - - RangeMin=UTILS.NMToMeters(RangeMin or 0) - RangeMax=UTILS.NMToMeters(RangeMax or 10) - - local weapon={} --Ops.OpsGroup#OPSGROUP.WeaponData - - weapon.BitType=BitType or ENUMS.WeaponFlag.Auto - weapon.RangeMax=RangeMax - weapon.RangeMin=RangeMin - - self.weaponData=self.weaponData or {} - self.weaponData[tostring(weapon.BitType)]=weapon - - -- Debug info. - self:T(self.lid..string.format("Adding weapon data: Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapon.BitType), weapon.RangeMin, weapon.RangeMax)) - - if self.verbose>=2 then - local text="Weapon data:" - for _,_weapondata in pairs(self.weaponData) do - local weapondata=_weapondata - text=text..string.format("\n- Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapondata.BitType), weapondata.RangeMin, weapondata.RangeMax) - end - self:I(self.lid..text) - end - - return self -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -156,7 +122,6 @@ function PLATOON:onafterStatus(From, Event, To) local fsmstate=self:GetState() local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A" - local modex=self.modex and self.modex or -1 local skill=self.skill and tostring(self.skill) or "N/A" local NassetsTot=#self.assets @@ -167,8 +132,8 @@ function PLATOON:onafterStatus(From, Event, To) end -- Short info. - local text=string.format("%s [Type=%s, Call=%s, Modex=%d, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]", - fsmstate, self.aircrafttype, callsign, modex, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ) + local text=string.format("%s [Type=%s, Call=%s, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]", + fsmstate, self.aircrafttype, callsign, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ) self:T(self.lid..text) -- Weapon data info. diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index 757b67ea1..07eae9dc2 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -126,6 +126,8 @@ ENUMS.WeaponFlag={ AnyMissile = 268402688, -- AnyASM + AnyAAM --- Guns Cannons = 805306368, -- GUN_POD + BuiltInCannon + --- Torpedo + Torpedo = 4294967296, --- -- Even More Genral Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons) @@ -136,6 +138,93 @@ ENUMS.WeaponFlag={ AnyGuided = 268402702, -- Any Guided Weapon } +--- Weapon types by category. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on hoggit wiki. +-- @type ENUMS.WeaponType +-- @field #table Bomb Bombs. +-- @field #table Rocket Rocket. +-- @field #table Gun Guns. +-- @field #table Missile Missiles. +-- @field #table AAM Air-to-Air missiles. +-- @field #table Torpedo Torpedos. +-- @field #table Any Combinations. +ENUMS.WeaponType={} +ENUMS.WeaponType.Bomb={ + -- Bombs + LGB = 2, + TvGB = 4, + SNSGB = 8, + HEBomb = 16, + Penetrator = 32, + NapalmBomb = 64, + FAEBomb = 128, + ClusterBomb = 256, + Dispencer = 512, + CandleBomb = 1024, + ParachuteBomb = 2147483648, + -- Combinations + GuidedBomb = 14, -- (LGB + TvGB + SNSGB) + AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb) + AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb) +} +ENUMS.WeaponType.Rocket={ + -- Rockets + LightRocket = 2048, + MarkerRocket = 4096, + CandleRocket = 8192, + HeavyRocket = 16384, + -- Combinations + AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket +} +ENUMS.WeaponType.Gun={ + -- Guns + GunPod = 268435456, + BuiltInCannon = 536870912, + -- Combinations + Cannons = 805306368, -- GUN_POD + BuiltInCannon +} +ENUMS.WeaponType.Missile={ + -- Missiles + AntiRadarMissile = 32768, + AntiShipMissile = 65536, + AntiTankMissile = 131072, + FireAndForgetASM = 262144, + LaserASM = 524288, + TeleASM = 1048576, + CruiseMissile = 2097152, + AntiRadarMissile2 = 1073741824, + -- Combinations + GuidedASM = 1572864, -- (LaserASM + TeleASM) + TacticalASM = 1835008, -- (GuidedASM + FireAndForgetASM) + AnyASM = 4161536, -- (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile) + AnyASM2 = 1077903360, -- 4161536+1073741824, + AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile + AnyMissile = 268402688, -- AnyASM + AnyAAM +} +ENUMS.WeaponType.AAM={ + -- Air-To-Air Missiles + SRAM = 4194304, + MRAAM = 8388608, + LRAAM = 16777216, + IR_AAM = 33554432, + SAR_AAM = 67108864, + AR_AAM = 134217728, + -- Combinations + AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM +} +ENUMS.WeaponType.Torpedo={ + -- Torpedo + Torpedo = 4294967296, +} +ENUMS.WeaponType.Any={ + -- General combinations + Weapon = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons) + AG = 2956984318, -- Any Air-To-Ground Weapon + AA = 264241152, -- Any Air-To-Air Weapon + Unguided = 2952822768, -- Any Unguided Weapon + Guided = 268402702, -- Any Guided Weapon +} + + --- Mission tasks. -- @type ENUMS.MissionTask -- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.