From 1e0a0f8dc5c2ff16099da4ddf7128394ed586390 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 13 Nov 2020 23:26:56 +0100 Subject: [PATCH] Ops --- Moose Development/Moose/Core/Zone.lua | 26 +++ .../Moose/Functional/ZoneCaptureCoalition.lua | 12 +- Moose Development/Moose/Ops/ArmyGroup.lua | 158 +++++++++++++++++- Moose Development/Moose/Ops/OpsGroup.lua | 85 +++++++++- Moose Development/Moose/Ops/Target.lua | 14 +- 5 files changed, 279 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index feda32b72..88d487622 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -817,6 +817,32 @@ function ZONE_RADIUS:GetScannedSetUnit() return SetUnit end +--- Get a set of scanned units. +-- @param #ZONE_RADIUS self +-- @return Core.Set#SET_GROUP Set of groups. +function ZONE_RADIUS:GetScannedSetGroup() + + self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New() --Core.Set#SET_GROUP + + self.ScanSetGroup.Set={} + + if self.ScanData then + for ObjectID, UnitObject in pairs( self.ScanData.Units ) do + local UnitObject = UnitObject -- DCS#Unit + if UnitObject:isExist() then + + local FoundUnit=UNIT:FindByName(UnitObject:getName()) + if FoundUnit then + local group=FoundUnit:GetGroup() + self.ScanSetGroup:AddGroup(group) + end + end + end + end + + return self.ScanSetGroup +end + --- Count the number of different coalitions inside the zone. -- @param #ZONE_RADIUS self diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index e6a07e323..07eea7e21 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -890,12 +890,14 @@ do -- ZONE_CAPTURE_COALITION end -- Status text. - local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State) - local NewState = self:GetState() - if NewState~=State then - text=text..string.format(" --> %s", NewState) + if false then + local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State) + local NewState = self:GetState() + if NewState~=State then + text=text..string.format(" --> %s", NewState) + end + self:I(text) end - self:I(text) end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 098cbab22..19cebb545 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -34,6 +34,8 @@ -- @field #boolean formationPerma Formation that is used permanently and overrules waypoint formations. -- @field #boolean isMobile If true, group is mobile. -- @field #ARMYGROUP.Target engage Engage target. +-- @field #boolean retreatOnOutOfAmmo If true, the group will automatically retreat when out of ammo. Needs a retreat zone! +-- @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 @@ -70,12 +72,13 @@ ARMYGROUP = { --- Army Group version. -- @field #string version -ARMYGROUP.version="0.3.0" +ARMYGROUP.version="0.4.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Retreat. -- TODO: Suppression of fire. -- TODO: Check if group is mobile. -- TODO: F10 menu. @@ -102,6 +105,7 @@ function ARMYGROUP:New(Group) self:SetDefaultAlarmstate() self:SetDetection() self:SetPatrolAdInfinitum(false) + self:SetRetreatZones() -- Add FSM transitions. -- From State --> Event --> To State @@ -112,10 +116,11 @@ function ARMYGROUP:New(Group) self:AddTransition("OnDetour", "DetourReached", "Cruising") -- Group reached the detour coordinate. self:AddTransition("*", "Retreat", "Retreating") -- - self:AddTransition("Retreating", "Retreated", "Holding") -- + self:AddTransition("Retreating", "Retreated", "Retreated") -- self:AddTransition("Cruising", "EngageTarget", "Engaging") -- Engage a target self:AddTransition("Holding", "EngageTarget", "Engaging") -- Engage a target + self:AddTransition("OnDetour", "EngageTarget", "Engaging") -- Engage a target self:AddTransition("Engaging", "Disengage", "Cruising") -- Engage a target self:AddTransition("*", "Rearm", "Rearm") -- Group is send to a coordinate and waits until ammo is refilled. @@ -258,6 +263,24 @@ function ARMYGROUP:AddTaskAttackGroup(TargetGroup, WeaponExpend, WeaponType, Clo return task end +--- Define a set of possible retreat zones. +-- @param #ARMYGROUP self +-- @param Core.Set#SET_ZONE RetreatZoneSet The retreat zone set. Default is an empty set. +-- @return #ARMYGROUP self +function ARMYGROUP:SetRetreatZones(RetreatZoneSet) + self.retreatZones=RetreatZoneSet or SET_ZONE:New() + return self +end + +--- Add a zone to the retreat zone set. +-- @param #ARMYGROUP self +-- @param Core.Zone#ZONE_BASE RetreatZone The retreat zone. +-- @return #ARMYGROUP self +function ARMYGROUP:AddRetreatZone(RetreatZone) + self.retreatZones:AddZone(RetreatZone) + return self +end + --- Check if the group is currently holding its positon. -- @param #ARMYGROUP self -- @return #boolean If true, group was ordered to hold. @@ -279,6 +302,20 @@ function ARMYGROUP:IsOnDetour() return self:Is("OnDetour") end +--- Check if the group is ready for combat. I.e. not reaming, retreating, retreated, out of ammo or engaging. +-- @param #ARMYGROUP self +-- @return #boolean If true, group is on a combat ready. +function ARMYGROUP:IsCombatReady() + local combatready=true + + if self:IsRearming() or self:IsRetreating() or self.outofAmmo or self:IsEngaging() or self:is("Retreated") or self:IsDead() or self:IsStopped() or self:IsInUtero() then + combatready=false + end + + return combatready +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -325,6 +362,9 @@ function ARMYGROUP:onafterStatus(From, Event, To) -- Check if group got stuck. self:_CheckStuck() + -- Check damage of elements and group. + self:_CheckDamage() + -- Update engagement. if self:IsEngaging() then self:_UpdateEngageTarget() @@ -344,8 +384,8 @@ function ARMYGROUP:onafterStatus(From, Event, To) local ammo=self:GetAmmoTot() -- Info text. - local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Speed=%.1f (%d), Heading=%03d, Ammo=%d", - fsmstate, roe, alarm, nTaskTot, nMissions, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), tostring(self.passedfinalwp), speed, speedEx, self.heading, ammo.Total) + local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Life=%.1f, Speed=%.1f (%d), Heading=%03d, Ammo=%d", + fsmstate, roe, alarm, nTaskTot, nMissions, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), tostring(self.passedfinalwp), self.life or 0, speed, speedEx, self.heading, ammo.Total) self:I(self.lid..text) end @@ -596,6 +636,13 @@ end -- @param #number ResumeRoute If true, resume route after detour point was reached. If false, the group will stop at the detour point and wait for futher commands. function ARMYGROUP:onafterDetour(From, Event, To, Coordinate, Speed, Formation, ResumeRoute) + for _,_wp in pairs(self.waypoints) do + local wp=_wp --Ops.OpsGroup#OPSGROUP.Waypoint + if wp.detour then + self:RemoveWaypointByID(wp.uid) + end + end + -- Speed in knots. Speed=Speed or self:GetSpeedCruise() @@ -652,6 +699,103 @@ function ARMYGROUP:onafterRearming(From, Event, To) end +--- On before "Retreat" event. +-- @param #ARMYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE_BASE Zone (Optional) Zone where to retreat. Default is the closest retreat zone. +-- @param #number Formation (Optional) Formation of the group. +function ARMYGROUP:onbeforeRetreat(From, Event, To, Zone, Formation) + + if not Zone then + + local a=self:GetVec2() + + local distmin=math.huge + local zonemin=nil + for _,_zone in pairs(self.retreatZones:GetSet()) do + local zone=_zone --Core.Zone#ZONE_BASE + + local b=zone:GetVec2() + + local dist=UTILS.VecDist2D(a, b) + + if dist Stop! + self:Route({wp}) + +end + +--- On after "EngageTarget" event. +-- @param #ARMYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP Group the group to be engaged. +function ARMYGROUP:onbeforeEngageTarget(From, Event, To, Target) + + local ammo=self:GetAmmoTot() + + if ammo.Total==0 then + env.info("FF cannot engage because no ammo!") + return false + end + + return true +end + --- On after "EngageTarget" event. -- @param #ARMYGROUP self -- @param #string From From state. @@ -1031,11 +1175,13 @@ function ARMYGROUP:_InitGroup() element.categoryname=element.unit:GetCategoryName() element.size, element.length, element.height, element.width=unit:GetObjectSize() element.ammo0=self:GetAmmoUnit(unit, false) + element.life0=unit:GetLife0() + element.life=element.life0 -- Debug text. if self.verbose>=2 then - local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", - element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width) + local text=string.format("Adding element %s: status=%s, skill=%s, life=%.3f category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", + element.name, element.status, element.skill, element.life, element.categoryname, element.category, element.size, element.length, element.height, element.width) self:I(self.lid..text) end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index f032d03ae..4256fe735 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -156,6 +156,8 @@ OPSGROUP = { -- @field #number length Length of element in meters. -- @field #number width Width of element in meters. -- @field #number height Height of element in meters. +-- @field #number life0 Initial life points. +-- @field #number life Life points when last updated. --- Status of group element. -- @type OPSGROUP.ElementStatus @@ -391,10 +393,13 @@ function OPSGROUP:New(Group) -- Add FSM transitions. -- From State --> Event --> To State self:AddTransition("InUtero", "Spawned", "Spawned") -- The whole group was spawned. - self:AddTransition("*", "Dead", "Dead") -- The whole group is dead. + self:AddTransition("*", "Dead", "Dead") -- The whole group is dead. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Status", "*") -- Status update. + + self:AddTransition("*", "Destroyed", "*") -- The whole group is dead. + self:AddTransition("*", "Damaged", "*") -- Someone in the group took damage. self:AddTransition("*", "UpdateRoute", "*") -- Update route of group. Only if airborne. self:AddTransition("*", "Respawn", "*") -- Respawn group. @@ -445,6 +450,7 @@ function OPSGROUP:New(Group) self:AddTransition("*", "ElementSpawned", "*") -- An element was spawned. self:AddTransition("*", "ElementDestroyed", "*") -- An element was destroyed. self:AddTransition("*", "ElementDead", "*") -- An element is dead. + self:AddTransition("*", "ElementDamaged", "*") -- An element was damaged. ------------------------ --- Pseudo Functions --- @@ -936,8 +942,23 @@ function OPSGROUP:DespawnElement(Element, Delay, NoEventRemoveUnit) return self end +--- Get current 2D position vector of the group. +-- @param #OPSGROUP self +-- @return DCS#Vec2 Vector with x,y components. +function OPSGROUP:GetVec2() ---- Get current 3D vector of the group. + local vec3=self:GetVec3() + + if vec3 then + local vec2={x=vec3.x, y=vec3.z} + return vec2 + end + + return nil +end + + +--- Get current 3D position vector of the group. -- @param #OPSGROUP self -- @return DCS#Vec3 Vector with x,y,z components. function OPSGROUP:GetVec3() @@ -973,6 +994,7 @@ function OPSGROUP:GetCoordinate(NewObject) if NewObject then local coord=COORDINATE:NewFromCoordinate(self.coordinate) + return coord else return self.coordinate end @@ -1245,9 +1267,16 @@ function OPSGROUP:IsLasing() return self.spot.On end ---- Check if the group has currently switched a LASER on. +--- Check if the group is currently retreating. -- @param #OPSGROUP self --- @return #boolean If true, LASER of the group is on. +-- @return #boolean If true, group is retreating. +function OPSGROUP:IsRetreating() + return self:is("Retreating") +end + +--- Check if the group is engaging another unit or group. +-- @param #OPSGROUP self +-- @return #boolean If true, group is engaging. function OPSGROUP:IsEngaging() return self:is("Engaging") end @@ -3695,6 +3724,17 @@ function OPSGROUP:onafterElementDead(From, Event, To, Element) end +--- On before "Dead" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onbeforeDead(From, Event, To) + if self.Ndestroyed==#self.elements then + self:Destroyed() + end +end + --- On after "Dead" event. -- @param #OPSGROUP self -- @param #string From From state. @@ -4052,6 +4092,37 @@ function OPSGROUP:_CheckStuck() end + +--- Check damage. +-- @param #OPSGROUP self +-- @return #OPSGROUP self +function OPSGROUP:_CheckDamage() + + self.life=0 + local damaged=false + for _,_element in pairs(self.elements) do + local element=_element --Ops.OpsGroup#OPSGROUP + + -- Current life points. + local life=element.unit:GetLife() + + self.life=self.life+life + + if life