This commit is contained in:
Frank 2020-11-13 23:26:56 +01:00
parent f3d9b9c5be
commit 1e0a0f8dc5
5 changed files with 279 additions and 16 deletions

View File

@ -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

View File

@ -890,12 +890,14 @@ do -- ZONE_CAPTURE_COALITION
end
-- Status text.
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
end

View File

@ -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<distmin then
distmin=dist
zonemin=zone
end
end
if zonemin then
self:__Retreat(0.1, zonemin, Formation)
end
return false
end
return true
end
--- On after "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:onafterRetreat(From, Event, To, Zone, Formation)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
local Coordinate=Zone:GetRandomCoordinate()
-- Add waypoint after current.
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
-- Set if we want to resume route after reaching the detour waypoint.
wp.detour=0
end
--- On after "Retreated" event.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function ARMYGROUP:onafterRetreated(From, Event, To)
-- Get current position.
local pos=self:GetCoordinate()
-- Create a new waypoint.
local wp=pos:WaypointGround(0)
-- Create new route consisting of only this position ==> 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

View File

@ -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
@ -396,6 +398,9 @@ function OPSGROUP:New(Group)
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.
self:AddTransition("*", "PassingWaypoint", "*") -- Passing waypoint.
@ -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<element.life then
element.life=life
self:ElementDamaged(element)
damaged=true
end
end
if damaged then
self:Damaged()
end
return self
end
--- Check ammo is full.
-- @param #OPSGROUP self
-- @return #boolean If true, ammo is full.
@ -4094,6 +4165,7 @@ function OPSGROUP:_CheckAmmoStatus()
self.outofAmmo=false
end
if ammo.Total==0 and not self.outofAmmo then
env.info("FF out of ammo")
self.outofAmmo=true
self:OutOfAmmo()
end
@ -4443,6 +4515,11 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
-- Trigger Rearming event.
opsgroup:Rearming()
elseif opsgroup:IsRetreating() then
-- Trigger Retreated event.
opsgroup:Retreated()
elseif opsgroup:IsEngaging() then
-- Nothing to do really.

View File

@ -232,6 +232,12 @@ function TARGET:AddObject(Object)
self:AddObject(object)
end
elseif Object:IsInstanceOf("GROUP") then
for _,unit in pairs(Object:GetUnits()) do
self:_AddObject(unit)
end
else
---
@ -716,6 +722,8 @@ function TARGET:GetTargetVec3(Target)
if object and object:IsAlive() then
return object:GetVec3()
else
return nil
end
elseif Target.Type==TARGET.ObjectType.STATIC then
@ -724,6 +732,8 @@ function TARGET:GetTargetVec3(Target)
if object and object:IsAlive() then
return object:GetVec3()
else
return nil
end
elseif Target.Type==TARGET.ObjectType.SCENERY then
@ -732,6 +742,8 @@ function TARGET:GetTargetVec3(Target)
if object then
return object:GetVec3()
else
return nil
end
elseif Target.Type==TARGET.ObjectType.AIRBASE then