From 1a534108bf8afc050a6e466be8ff7edfcbc20dd3 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 24 Jun 2019 14:19:36 +0200 Subject: [PATCH] Updates ARTY v1.1.0 - added attack group - target data struckture WAREHOUSE v0.9.4 - assignment as descriptor AIRBOSS v1.0.3 - removed warehouse table - no stoping of recovery window if pattern is not empty - changes in patrol route --- .../Moose/Functional/Artillery.lua | 156 +++++++++++++++- .../Moose/Functional/Warehouse.lua | 19 +- Moose Development/Moose/Ops/Airboss.lua | 172 ++++++++++++++---- 3 files changed, 303 insertions(+), 44 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 637560d03..9e389bebe 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -40,7 +40,7 @@ -- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players. -- @field #table targets All targets assigned. -- @field #table moves All moves assigned. --- @field #table currentTarget Holds the current target, if there is one assigned. +-- @field #ARTY.Target currentTarget Holds the current target, if there is one assigned. -- @field #table currentMove Holds the current commanded move, if there is one assigned. -- @field #number Nammo0 Initial amount total ammunition (shells+rockets+missiles) of the whole group. -- @field #number Nshells0 Initial amount of shells of the whole group. @@ -668,13 +668,28 @@ ARTY.db={ }, } +--- Target. +-- @type ARTY.Target +-- @field #string name Name of target. +-- @field Core.Point#COORDINATE coord Target coordinates. +-- @field #number radius Shelling radius in meters. +-- @field #number nshells Number of shells (or other weapon types) fired upon target. +-- @field #number engaged Number of times this target was engaged. +-- @field #boolean underfire If true, target is currently under fire. +-- @field #number prio Priority of target. +-- @field #number maxengage Max number of times, the target will be engaged. +-- @field #number time Abs. mission time in seconds, when the target is scheduled to be attacked. +-- @field #number weapontype Type of weapon used for engagement. See #ARTY.WeaponType. +-- @field #number Tassigned Abs. mission time when target was assigned. +-- @field #boolean attackgroup If true, use task attack group rather than fire at point for engagement. + --- Some ID to identify who we are in output of the DCS.log file. -- @field #string id ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="1.0.7" +ARTY.version="1.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1209,6 +1224,97 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w return _name end +--- Assign a target group to the ARTY group. Note that this will use the Attack Group Task rather than the Fire At Point Task. +-- @param #ARTY self +-- @param Wrapper.Group#GROUP group Target group. +-- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. +-- @param #number radius (Optional) Radius. Default is 100 m. +-- @param #number nshells (Optional) How many shells (or rockets) are fired on target per engagement. Default 5. +-- @param #number maxengage (Optional) How many times a target is engaged. Default 1. +-- @param #string time (Optional) Day time at which the target should be engaged. Passed as a string in format "08:13:45". Current task will be canceled. +-- @param #number weapontype (Optional) Type of weapon to be used to attack this target. Default ARTY.WeaponType.Auto, i.e. the DCS logic automatically determins the appropriate weapon. +-- @param #string name (Optional) Name of the target. Default is LL DMS coordinate of the target. If the name was already given, the numbering "#01", "#02",... is appended automatically. +-- @param #boolean unique (Optional) Target is unique. If the target name is already known, the target is rejected. Default false. +-- @return #string Name of the target. Can be used for further reference, e.g. deleting the target from the list. +-- @usage paladin=ARTY:New(GROUP:FindByName("Blue Paladin")) +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 10, 300, 10, 1, "08:02:00", ARTY.WeaponType.Auto, "Target 1") +-- paladin:Start() +function ARTY:AssignAttackGroup(group, prio, radius, nshells, maxengage, time, weapontype, name, unique) + + -- Set default values. + nshells=nshells or 5 + radius=radius or 100 + maxengage=maxengage or 1 + prio=prio or 50 + prio=math.max( 1, prio) + prio=math.min(100, prio) + if unique==nil then + unique=false + end + weapontype=weapontype or ARTY.WeaponType.Auto + + -- TODO Check if we have a group object. + if type(group)=="string" then + group=GROUP:FindByName(group) + end + + if group and group:IsAlive() then + + local coord=group:GetCoordinate() + + -- Name of the target. + local _name=group:GetName() + local _unique=true + + -- Check if the name has already been used for another target. If so, the function returns a new unique name. + _name,_unique=self:_CheckName(self.targets, _name, not unique) + + -- Target name should be unique and is not. + if unique==true and _unique==false then + self:T(ARTY.id..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!", self.groupname, _name)) + return nil + end + + -- Time in seconds. + local _time + if type(time)=="string" then + _time=self:_ClockToSeconds(time) + elseif type(time)=="number" then + _time=timer.getAbsTime()+time + else + _time=timer.getAbsTime() + end + + -- Prepare target array. + local target={} --#ARTY.Target + target.attackgroup=true + target.name=_name + target.coord=coord + target.radius=radius + target.nshells=nshells + target.engaged=0 + target.underfire=false + target.prio=prio + target.time=_time + target.maxengage=maxengage + target.weapontype=weapontype + + -- Add to table. + table.insert(self.targets, target) + + -- Trigger new target event. + self:__NewTarget(1, target) + + return _name + else + self:E("ERROR: Group does not exist!") + end + + return nil +end + + + --- Assign coordinate to where the ARTY group should move. -- @param #ARTY self -- @param Core.Point#COORDINATE coord Coordinates of the new position. @@ -2766,7 +2872,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #table target Array holding the target info. +-- @param #ARTY.Target target Array holding the target info. function ARTY:onafterOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onafterOpenFire", Event, From, To) @@ -2828,7 +2934,11 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) --end -- Start firing. - self:_FireAtCoord(target.coord, target.radius, target.nshells, target.weapontype) + if target.attackgroup then + self:_AttackGroup(target) + else + self:_FireAtCoord(target.coord, target.radius, target.nshells, target.weapontype) + end end @@ -3321,6 +3431,39 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) group:SetTask(fire) end +--- Set task for firing at a coordinate. +-- @param #ARTY self +-- @param #ARTY.Target target Target data. +function ARTY:_AttackGroup(target) + + -- Controllable. + local group=self.Controllable --Wrapper.Group#GROUP + + local weapontype=target.weapontype + + -- Tactical nukes are actually cannon shells. + if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then + weapontype=ARTY.WeaponType.Cannon + end + + -- Set ROE to weapon free. + group:OptionROEOpenFire() + + -- Target group. + local targetgroup=GROUP:FindByName(target.name) + + -- Get task. + local fire=group:TaskAttackGroup(targetgroup, weapontype, AI.Task.WeaponExpend.ONE, 1) + + self:E("FF") + self:E(fire) + + -- Execute task. + group:PushTask(fire) +end + + + --- Model a nuclear blast/destruction by creating fires and destroy scenery. -- @param #ARTY self -- @param Core.Point#COORDINATE _coord Coordinate of the impact point (center of the blast). @@ -4860,13 +5003,14 @@ end --- Returns the target parameters as formatted string. -- @param #ARTY self +-- @param #ARTY.Target target The target data. -- @return #string name, prio, radius, nshells, engaged, maxengage, time, weapontype function ARTY:_TargetInfo(target) local clock=tostring(self:_SecondsToClock(target.time)) local weapon=self:_WeaponTypeName(target.weapontype) local _underfire=tostring(target.underfire) - return string.format("%s: prio=%d, radius=%d, nshells=%d, engaged=%d/%d, weapontype=%s, time=%s, underfire=%s", - target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, weapon, clock,_underfire) + return string.format("%s: prio=%d, radius=%d, nshells=%d, engaged=%d/%d, weapontype=%s, time=%s, underfire=%s, attackgroup=%s", + target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, weapon, clock,_underfire, tostring(target.attackgroup)) end --- Returns a formatted string with information about all move parameters. diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 802e22275..0ff01cfbd 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1634,11 +1634,13 @@ WAREHOUSE = { -- @field #string UNITTYPE Typename of the DCS unit, e.g. "A-10C". -- @field #string ATTRIBUTE Generalized attribute @{#WAREHOUSE.Attribute}. -- @field #string CATEGORY Asset category of type DCS#Group.Category, i.e. GROUND, AIRPLANE, HELICOPTER, SHIP, TRAIN. +-- @field #string ASSIGNMENT Assignment of asset when it was added. WAREHOUSE.Descriptor = { GROUPNAME="templatename", UNITTYPE="unittype", ATTRIBUTE="attribute", CATEGORY="category", + ASSIGNMENT="assignment", } --- Generalized asset attributes. Can be used to request assets with certain general characteristics. See [DCS attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes) on hoggit. @@ -1743,7 +1745,7 @@ _WAREHOUSEDB = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.9.2" +WAREHOUSE.version="0.9.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3684,7 +3686,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu self:_DebugMessage(string.format("Warehouse %s: Adding %d NEW assets of group %s to stock.", self.alias, n, tostring(group:GetName())), 5) -- This is a group that is not in the db yet. Add it n times. - local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill) + local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill, assignment) -- Add created assets to stock of this warehouse. for _,asset in pairs(assets) do @@ -3720,8 +3722,9 @@ end -- @param #number loadradius Radius in meters when cargo is loaded into the carrier. -- @param #table liveries Table of liveries. -- @param DCS#AI.Skill skill Skill of AI. +-- @param #string assignment Assignment attached to the asset item. -- @return #table A table containing all registered assets. -function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill) +function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill, assignment) self:F({groupname=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. @@ -3823,6 +3826,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, asset.livery=liveries[math.random(#liveries)] end asset.skill=skill + asset.assignment=assignment if i==1 then self:_AssetItemInfo(asset) @@ -3937,8 +3941,15 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto okay=false end + elseif AssetDescriptor==WAREHOUSE.Descriptor.ASSIGNMENT then + + if type(AssetDescriptorValue)~="string" then + self:_ErrorMessage("ERROR: Invalid request. Asset assignment type must be passed as a string!", 5) + okay=false + end + else - self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, GROUPNAME or UNITTYPE!", 5) + self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, GROUPNAME, UNITTYPE or ASSIGNMENT!", 5) okay=false end diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index e6b13ad69..385016112 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -16,7 +16,7 @@ -- * Advanced F10 radio menu including carrier info, weather, radio frequencies, TACAN/ICLS channels, player LSO grades, marking of zones etc. -- * Recovery tanker and refueling option via integration of @{Ops.RecoveryTanker} class. -- * Rescue helicopter option via @{Ops.RescueHelo} class. --- * Combine multiple human players to sections (WIP). +-- * Combine multiple human players to sections. -- * Many parameters customizable by convenient user API functions. -- * Multiple carrier support due to object oriented approach. -- * Unlimited number of players. @@ -32,7 +32,7 @@ -- **Supported Aircraft:** -- -- * [F/A-18C Hornet Lot 20](https://forums.eagle.ru/forumdisplay.php?f=557) (Player & AI) --- * [F-14B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI) [**WIP**] +-- * [F-14B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI) -- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI) -- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI) [**WIP**] -- * F/A-18C Hornet (AI) @@ -46,10 +46,8 @@ -- the no other fixed wing aircraft (human or AI controlled) are supposed to land on the Tarawa. Currently only Case I is supported. Case II/III take slightly steps from the CVN carrier. -- However, the two Case II/III pattern are very similar so this is not a big drawback. -- --- Heatblur's mighty F-14B Tomcat has just been added (March 13th 2019). Beware that this is currently WIP - both the module and the AIRBOSS implementation. +-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. -- --- **PLEASE NOTE** that his class is work in progress. Many/most things work already very nicely but there a lot of cases I did not run into yet. --- Therefore, your *constructive* feedback is both necessary and appreciated! -- -- ## Discussion -- @@ -174,7 +172,6 @@ -- @field #number NmaxStack Number of max flights per stack. Default 2. -- @field #boolean handleai If true (default), handle AI aircraft. -- @field Ops.RecoveryTanker#RECOVERYTANKER tanker Recovery tanker flying overhead of carrier. --- @field Functional.Warehouse#WAREHOUSE warehouse Warehouse object of the carrier. -- @field DCS#Vec3 Corientation Carrier orientation in space. -- @field DCS#Vec3 Corientlast Last known carrier orientation. -- @field Core.Point#COORDINATE Cposition Carrier position. @@ -1182,7 +1179,6 @@ AIRBOSS = { NmaxStack = nil, handleai = nil, tanker = nil, - warehouse = nil, Corientation = nil, Corientlast = nil, Cposition = nil, @@ -1685,7 +1681,7 @@ AIRBOSS.MenuF10Root=nil --- Airboss class version. -- @field #string version -AIRBOSS.version="1.0.2" +AIRBOSS.version="1.0.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1802,6 +1798,15 @@ function AIRBOSS:New(carriername, alias) -- Init player scores table. self.playerscores={} + + -- Initialize ME waypoints. + self:_InitWaypoints() + + -- Current waypoint. + self.currentwp=1 + + -- Patrol route. + self:_PatrolRoute() ------------- --- Defaults: @@ -2432,6 +2437,19 @@ function AIRBOSS:SetExcludeAI(setgroup) return self end +--- Add a group to the exclude set. If no set exists, it is created. +-- @param #AIRBOSS self +-- @param Wrapper.Group#GROUP group The group to be excluded. +-- @return #AIRBOSS self +function AIRBOSS:AddExcludeAI(group) + + self.excludesetAI=self.excludesetAI or SET_GROUP:New() + + self.excludesetAI:AddGroup(group) + + return self +end + --- Close currently running recovery window and stop recovery ops. Recovery window is deleted. -- @param #AIRBOSS self -- @param #number delay (Optional) Delay in seconds before the window is deleted. @@ -3044,15 +3062,6 @@ function AIRBOSS:SetRecoveryTanker(recoverytanker) return self end ---- Define warehouse associated with the carrier. --- @param #AIRBOSS self --- @param Functional.Warehouse#WAREHOUSE warehouse Warehouse object of the carrier. --- @return #AIRBOSS self -function AIRBOSS:SetWarehouse(warehouse) - self.warehouse=warehouse - return self -end - --- Set default player skill. New players will be initialized with this skill. -- -- * "Flight Student" = @{#AIRBOSS.Difficulty.Easy} @@ -3202,9 +3211,6 @@ function AIRBOSS:onafterStart(From, Event, To) self.Corientlast=self.Corientation self.Tpupdate=timer.getTime() - -- Init patrol route of carrier. - self:_PatrolRoute(1) - -- Check if no recovery window is set. DISABLED! if #self.recoverytimes==0 and false then @@ -3232,9 +3238,6 @@ function AIRBOSS:onafterStart(From, Event, To) self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) self:HandleEvent(EVENTS.MissionEnd) - -- DCS event handler. - --world.addEventHandler(self) - -- Start status check in 1 second. self:__Status(1) end @@ -3617,15 +3620,27 @@ function AIRBOSS:_CheckRecoveryTimes() if self:IsRecovering() and not recovery.OVER then - -- Set carrier to idle. - self:RecoveryStop() - state="closing now" + if #self.Qpattern>0 then + + local extmin=5*#self.Qpattern + recovery.STOP=recovery.STOP+extmin*60 + + local text=string.format("We still got flights in the pattern.\nRecovery time prolonged by %d minutes.\nNow get your act together and no more bolters!", extmin) + self:MessageToPattern(text, "AIRBOSS", "99", 10, false, nil) - -- Closed. - recovery.OPEN=false + else + + -- Set carrier to idle. + self:RecoveryStop() + state="closing now" + + -- Closed. + recovery.OPEN=false + + -- Window just closed. + recovery.OVER=true - -- Window just closed. - recovery.OVER=true + end else -- Carrier is already idle. @@ -3689,10 +3704,11 @@ function AIRBOSS:_CheckRecoveryTimes() --Debug info self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s", hdg, wind,UTILS.MpsToKnots(vwind), delta, tostring(uturn))) + + -- Time into the wind 1 day or if longer recovery time + the 5 min early. + local t=math.max(nextwindow.STOP-nextwindow.START+300, 60*60*24) - - -- Time into the wind + the 5 min early. - local t=nextwindow.STOP-nextwindow.START+300 + -- Recovery wind on deck in knots. local v=UTILS.KnotsToMps(nextwindow.SPEED) -- Check that we do not go above max possible speed. @@ -13206,6 +13222,42 @@ function AIRBOSS:_GetNextWaypoint() return nextwp,Nextwp end + +--- Initialize Mission Editor waypoints. +-- @param #AIRBOSS self +-- @return #AIRBOSS self +function AIRBOSS:_InitWaypoints() + + -- Waypoints of group as defined in the ME. + local Waypoints=self.carrier:GetGroup():GetTemplateRoutePoints() + + -- Init array. + self.waypoints={} + + -- Set waypoint table. + for i,point in ipairs(Waypoints) do + + -- Coordinate of the waypoint + local coord=COORDINATE:New(point.x, point.alt, point.y) + + -- Set velocity of the coordinate. + coord:SetVelocity(point.speed) + + -- Add to table. + table.insert(self.waypoints, coord) + + -- Debug info. + if self.Debug then + coord:MarkToAll(string.format("Carrier Waypoint %d, Speed=%.1f knots", i, UTILS.MpsToKnots(point.speed))) + end + + end + + return self +end + +--[[ + --- Patrol carrier. -- @param #AIRBOSS self -- @param #number n Current waypoint. @@ -13259,6 +13311,58 @@ function AIRBOSS:_PatrolRoute(n) return self end +]] + +--- Patrol carrier. +-- @param #AIRBOSS self +-- @param #number n Next waypoint number. +-- @return #AIRBOSS self +function AIRBOSS:_PatrolRoute(n) + + -- Get next waypoint coordinate and number. + local nextWP, N=self:_GetNextWaypoint() + + -- Default resume is to next waypoint. + n=n or N + + -- Get carrier group. + local CarrierGroup=self.carrier:GetGroup() + + -- Waypoints table. + local Waypoints={} + + -- Create a waypoint from the current coordinate. + local wp=self:GetCoordinate():WaypointGround(CarrierGroup:GetVelocityKMH()) + + -- Add current position as first waypoint. + table.insert(Waypoints, wp) + + -- Loop over waypoints. + for i=n,#self.waypoints do + local coord=self.waypoints[i] --Core.Point#COORDINATE + + -- Create a waypoint from the coordinate. + local wp=coord:WaypointGround(UTILS.MpsToKmph(coord.Velocity)) + + -- Passing waypoint taskfunction + local TaskPassingWP=CarrierGroup:TaskFunction("AIRBOSS._PassingWaypoint", self, i, #self.waypoints) + + -- Call task function when carrier arrives at waypoint. + CarrierGroup:SetTaskWaypoint(wp, TaskPassingWP) + + -- Add waypoint to table. + table.insert(Waypoints, wp) + end + + -- Route carrier group. + CarrierGroup:Route(Waypoints) + + return self +end + + + + --- Estimated the carrier position at some point in the future given the current waypoints and speeds. -- @param #AIRBOSS self -- @return DCS#time ETA abs. time in seconds. @@ -13484,7 +13588,7 @@ function AIRBOSS._PassingWaypoint(group, airboss, i, final) -- If final waypoint reached, do route all over again. if i==final and final>1 and airboss.adinfinitum then - airboss:_PatrolRoute(i) + airboss:_PatrolRoute() end end