Suppression + RAT

This commit is contained in:
funkyfranky 2017-10-16 00:14:41 +02:00
parent 1e2a84608f
commit e1d12cbd8e
3 changed files with 605 additions and 153 deletions

View File

@ -1019,7 +1019,7 @@ function EVENT:onEvent( Event )
end
end
else
self:E( { EventMeta.Text, Event } )
self:T( { EventMeta.Text, Event } )
end
Event = nil

View File

@ -64,6 +64,7 @@
-- @type RAT
-- @field #string ClassName Name of the Class.
-- @field #boolean debug Turn debug messages on or off.
-- @field Core.Group#GROUP templategroup Group serving as template for the RAT aircraft.
-- @field #string alias Alias for spawned group.
-- @field #number spawndelay Delay time in seconds before first spawning happens.
-- @field #number spawninterval Interval between spawning units/groups. Note that we add a randomization of 50%.
@ -89,6 +90,8 @@
-- @field #table departure_ports Array containing the names of the destination airports.
-- @field #table destination_ports Array containing the names of the destination airports.
-- @field #table excluded_ports Array containing the names of explicitly excluded airports.
-- @field #table destination_zones Array containing the names of the destination zones.
-- @field #boolean destinationzone Destination is a zone and not an airport.
-- @field Core.Zone#ZONE departure_Azone Zone containing the departure airports.
-- @field Core.Zone#ZONE destination_Azone Zone containing the destination airports.
-- @field #table ratcraft Array with the spawned RAT aircraft.
@ -107,6 +110,8 @@
-- @field #table Menu F10 menu items for this RAT object.
-- @field #string SubMenuName Submenu name for RAT object.
-- @field #boolean respawn_at_landing Respawn aircraft the moment they land rather than at engine shutdown.
-- @field #boolean norespawn Aircraft will not be respawned after they have finished their route.
-- @field #boolean respawn_after_takeoff Aircraft will be respawned directly after take-off.
-- @field #number respawn_delay Delay in seconds until repawn happens after landing.
-- @field #table markerids Array with marker IDs.
-- @field #string livery Livery of the aircraft set by user.
@ -262,6 +267,7 @@
RAT={
ClassName = "RAT", -- Name of class: RAT = Random Air Traffic.
debug=false, -- Turn debug messages on or off.
templategroup=nil, -- Template group for the RAT aircraft.
alias=nil, -- Alias for spawned group.
spawndelay=5, -- Delay time in seconds before first spawning happens.
spawninterval=5, -- Interval between spawning units/groups. Note that we add a randomization of 50%.
@ -286,6 +292,8 @@ RAT={
departure_zones={}, -- Array containing the names of the departure zones.
departure_ports={}, -- Array containing the names of the departure airports.
destination_ports={}, -- Array containing the names of the destination airports.
destination_zones={}, -- Array containing the names of destination zones.
destinationzone=false, -- Destination is a zone and not an airport.
excluded_ports={}, -- Array containing the names of explicitly excluded airports.
departure_Azone=nil, -- Zone containing the departure airports.
destination_Azone=nil, -- Zone containing the destination airports.
@ -305,11 +313,16 @@ RAT={
Menu={}, -- F10 menu items for this RAT object.
SubMenuName=nil, -- Submenu name for RAT object.
respawn_at_landing=false, -- Respawn aircraft the moment they land rather than at engine shutdown.
norespawn=false, -- Aircraft will not get respawned.
respawn_after_takeoff=false, -- Aircraft will be respawned directly after takeoff.
respawn_delay=nil, -- Delay in seconds until repawn happens after landing.
markerids={}, -- Array with marker IDs.
livery=nil, -- Livery of the aircraft.
skill="High", -- Skill of AI.
ATCswitch=true, -- Enable ATC.
parking_id=nil,
argkey=nil,
arg={},
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -336,6 +349,7 @@ RAT.wp={
descent=7,
holding=8,
landing=9,
finalwp=10,
}
--- RAT friendly coalitions.
@ -459,6 +473,9 @@ function RAT:New(groupname, alias)
return nil
end
-- Store template group.
self.templategroup=GROUP:FindByName(groupname)
-- Set own coalition.
self.coalition=DCSgroup:getCoalition()
@ -529,6 +546,8 @@ function RAT:Spawn(naircraft)
text=text..string.format("Spawn delay: %4.1f\n", self.spawndelay)
text=text..string.format("Spawn interval: %4.1f\n", self.spawninterval)
text=text..string.format("Respawn after landing: %s\n", tostring(self.respawn_at_landing))
text=text..string.format("Respawning off: %s\n", tostring(self.norespawn))
text=text..string.format("Respawn after take-off: %s\n", tostring(self.respawn_after_takeoff))
text=text..string.format("Respawn delay: %s\n", tostring(self.respawn_delay))
text=text..string.format("ROE: %s\n", tostring(self.roe))
text=text..string.format("ROT: %s\n", tostring(self.rot))
@ -577,7 +596,7 @@ function RAT:Spawn(naircraft)
self:HandleEvent(EVENTS.Land, self._OnLand)
self:HandleEvent(EVENTS.EngineShutdown, self._OnEngineShutdown)
self:HandleEvent(EVENTS.Dead, self._OnDead)
self:HandleEvent(EVENTS.Crash, self._OnCrash)
--self:HandleEvent(EVENTS.Crash, self._OnCrash)
-- TODO: add hit event?
end
@ -734,6 +753,36 @@ function RAT:SetDestination(names)
end
--- Set name of destination zones for the AI aircraft. If multiple names are given as a table, one zone is picked randomly as destination.
-- @param #RAT self
-- @param #string names Name or table of names of zones defined in the mission editor.
function RAT:SetDestinationZone(names)
-- Random destination is deactivated now that user specified destination zone(s).
self.random_destination=false
-- Destination is a zone. Needs special care.
self.destinationzone=true
-- No ATC required.
self.ATCswitch=false
if type(names)=="table" then
for _,name in pairs(names) do
table.insert(self.destination_zones, ZONE:New(name))
end
elseif type(names)=="string" then
table.insert(self.destination_zones, ZONE:New(names))
else
-- Error message.
env.error("Input parameter must be a string or a table!")
end
end
--- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the airports lie.
@ -818,6 +867,26 @@ function RAT:RespawnAfterLanding(delay)
self.respawn_delay=delay
end
--- Aircraft will not get respawned when they finished their route.
-- @param #RAT self
function RAT:NoRespawn()
self.norespawn=true
end
--- Aircraft will be respawned directly after take-off.
-- @param #RAT self
function RAT:RespawnAfterTakeoff()
self.respawn_after_takeoff=true
end
--- Set parking id of aircraft.
-- @param #RAT self
-- @param #string id Parking ID of the aircraft.
function RAT:SetParkingID(id)
self.parking_id=id
env.info(RAT.id.."Setting parking ID to "..self.parking_id)
end
--- Set the time after which inactive groups will be destroyed. Default is 300 seconds.
-- @param #RAT self
-- @param #number time Time in seconds.
@ -1098,6 +1167,13 @@ function RAT:_SpawnWithRoute(_departure, _destination)
RAT:_ATCAddFlight(group:GetName(), destination:GetName())
end
if self.destinationzone then
-- env.info(RAT.id.." setstate")
-- self:E(self.argkey)
-- self:E(self.arg)
-- group:SetState(group, self.argkey, self.arg )
end
-- Set ROE, default is "weapon hold".
self:_SetROE(group, self.roe)
@ -1148,11 +1224,14 @@ function RAT:_SpawnWithRoute(_departure, _destination)
MENU_MISSION_COMMAND:New("Evade on fire", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"], self._SetROT, self, group, RAT.ROT.evade)
-- F10/RAT/<templatename>/Group X/
MENU_MISSION_COMMAND:New("Despawn group", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self._Despawn, self, group)
MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.ClearForLanding, self, group:GetName())
if self.ATCswitch then
MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.ClearForLanding, self, group:GetName())
end
MENU_MISSION_COMMAND:New("Place markers", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self._PlaceMarkers, self, waypoints)
MENU_MISSION_COMMAND:New("Status report", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.Status, self, true, self.SpawnIndex)
end
env.info("RAT debug before end of _SpawnWithRoute")
return self.SpawnIndex
end
@ -1502,26 +1581,46 @@ function RAT:_SetRoute(takeoff, _departure, _destination)
d_cruise=100
end
-- Coordinates of route from departure (0) to cruise (1) to descent (2) to holing (3) to destination (4).
local c0=Pdeparture
local c1=c0:Translate(d_climb/2, heading)
local c2=c1:Translate(d_climb/2, heading)
local c3=c2:Translate(d_cruise, heading)
local c4=c3:Translate(d_descent/2, heading)
local c5=Pholding
local c6=Pdestination
local waypoints
if not self.destinationzone then
--Convert coordinates into route waypoints.
local wp0=self:_Waypoint(takeoff, c0, VxClimb, H_departure, departure)
local wp1=self:_Waypoint(RAT.wp.climb, c1, VxClimb, H_departure+(FLcruise-H_departure)/2)
local wp2=self:_Waypoint(RAT.wp.cruise, c2, VxCruise, FLcruise)
local wp3=self:_Waypoint(RAT.wp.cruise, c3, VxCruise, FLcruise)
local wp4=self:_Waypoint(RAT.wp.descent, c4, VxDescent, FLcruise-(FLcruise-(h_holding+H_holding))/2)
local wp5=self:_Waypoint(RAT.wp.holding, c5, VxHolding, H_holding+h_holding)
local wp6=self:_Waypoint(RAT.wp.landing, c6, VxFinal, H_destination, destination)
-- Coordinates of route from departure (0) to cruise (1) to descent (2) to holing (3) to destination (4).
local c0=Pdeparture
local c1=c0:Translate(d_climb/2, heading)
local c2=c1:Translate(d_climb/2, heading)
local c3=c2:Translate(d_cruise, heading)
local c4=c3:Translate(d_descent/2, heading)
local c5=Pholding
local c6=Pdestination
-- set waypoints
local waypoints = {wp0, wp1, wp2, wp3, wp4, wp5, wp6}
--Convert coordinates into route waypoints.
local wp0=self:_Waypoint(takeoff, c0, VxClimb, H_departure, departure)
local wp1=self:_Waypoint(RAT.wp.climb, c1, VxClimb, H_departure+(FLcruise-H_departure)/2)
local wp2=self:_Waypoint(RAT.wp.cruise, c2, VxCruise, FLcruise)
local wp3=self:_Waypoint(RAT.wp.cruise, c3, VxCruise, FLcruise)
local wp4=self:_Waypoint(RAT.wp.descent, c4, VxDescent, FLcruise-(FLcruise-(h_holding+H_holding))/2)
local wp5=self:_Waypoint(RAT.wp.holding, c5, VxHolding, H_holding+h_holding)
local wp6=self:_Waypoint(RAT.wp.landing, c6, VxFinal, H_destination, destination)
-- set waypoints
waypoints = {wp0, wp1, wp2, wp3, wp4, wp5, wp6}
else
local c0=Pdeparture
local c1=c0:Translate(d_climb/2, heading)
local c2=c1:Translate(d_climb/2, heading)
local c3=Pdestination
local wp0=self:_Waypoint(takeoff, c0, VxClimb, H_departure, departure)
local wp1=self:_Waypoint(RAT.wp.climb, c1, VxClimb, H_departure+(FLcruise-H_departure)/2)
local wp2=self:_Waypoint(RAT.wp.cruise, c2, VxCruise, FLcruise)
local wp3=self:_Waypoint(RAT.wp.finalwp, c3, VxCruise, FLcruise)
-- set waypoints
waypoints = {wp0, wp1, wp2, wp3}
end
-- Place markers of waypoints on F10 map.
if self.placemarkers then
@ -1637,7 +1736,12 @@ function RAT:_PickDestination(destinations, _random)
destination=destinations[math.random(#destinations)] -- Wrapper.Airbase#AIRBASE
-- Debug message.
local text="Chosen destination airport: "..destination:GetName().." (ID "..destination:GetID()..")"
local text
if self.destinationzone then
text="Chosen destination zone: "..destination:GetName()
else
text="Chosen destination airport: "..destination:GetName().." (ID "..destination:GetID()..")"
end
env.info(RAT.id..text)
if self.debug then
MESSAGE:New(text, 30):ToAll()
@ -1686,13 +1790,23 @@ function RAT:_GetDestinations(departure, q, minrange, maxrange)
else
-- Airports specified by user.
for _,name in pairs(self.destination_ports) do
--if self:_IsFriendly(name) and not self:_Excluded(name) and name~=departure:GetName() then
if name~=departure:GetName() then
local airport=AIRBASE:FindByName(name)
--TODO: Maybe here I should check min/max distance as well? But the user explicitly specified the airports...
table.insert(possible_destinations, airport)
if self.destinationzone then
-- Zones specified by user.
for _,zone in pairs(self.destination_zones) do
table.insert(possible_destinations, zone)
end
else
-- Airports specified by user.
for _,name in pairs(self.destination_ports) do
--if self:_IsFriendly(name) and not self:_Excluded(name) and name~=departure:GetName() then
if name~=departure:GetName() then
local airport=AIRBASE:FindByName(name)
--TODO: Maybe here I should check min/max distance as well? But the user explicitly specified the airports...
table.insert(possible_destinations, airport)
end
end
end
@ -1713,7 +1827,7 @@ function RAT:_GetDestinations(departure, q, minrange, maxrange)
end
table.sort(possible_destinations, compare)
else
env.error(RAT.id.."No possible destination airports found!")
env.error(RAT.id.."No possible destinations found!")
possible_destinations=nil
end
@ -1933,7 +2047,11 @@ function RAT:Status(message, forID)
local Ddestination=Pn:Get2DDistance(self.ratcraft[i].destination:GetCoordinate())
-- Distance remaining to holding point, which is waypoint 6
local Hp=COORDINATE:New(self.ratcraft[i].waypoints[6].x, self.ratcraft[i].waypoints[6].alt, self.ratcraft[i].waypoints[6].y)
local idx=6
if self.destinationzone then
idx=4
end
local Hp=COORDINATE:New(self.ratcraft[i].waypoints[idx].x, self.ratcraft[i].waypoints[idx].alt, self.ratcraft[i].waypoints[idx].y)
local Dholding=Pn:Get2DDistance(Hp)
-- Status shortcut.
@ -2143,6 +2261,14 @@ function RAT:_OnTakeoff(EventData)
-- Set status.
self:_SetStatus(SpawnGroup, "On journey (after takeoff)")
if self.respawn_after_takeoff then
text="Event: Group "..SpawnGroup:GetName().." will be respawned."
env.info(RAT.id..text)
-- Respawn group.
self:_Respawn(SpawnGroup)
end
end
end
@ -2180,7 +2306,7 @@ function RAT:_OnLand(EventData)
RAT:_ATCFlightLanded(SpawnGroup:GetName())
end
if self.respawn_at_landing then
if self.respawn_at_landing and not self.norespawn then
text="Event: Group "..SpawnGroup:GetName().." will be respawned."
env.info(RAT.id..text)
@ -2220,7 +2346,7 @@ function RAT:_OnEngineShutdown(EventData)
-- Set status.
self:_SetStatus(SpawnGroup, "Parking (shutting down engines)")
if not self.respawn_at_landing then
if not self.respawn_at_landing and not self.norespawn then
text="Event: Group "..SpawnGroup:GetName().." will be respawned."
env.info(RAT.id..text)
@ -2281,10 +2407,10 @@ function RAT:_OnCrash(EventData)
local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP
env.info(string.format("%sGroup %s crashed!", RAT.id, SpawnGroup:GetName()))
if SpawnGroup then
env.info(string.format("%sGroup %s crashed!", RAT.id, SpawnGroup:GetName()))
-- Get the template name of the group. This can be nil if this was not a spawned group.
local EventPrefix = self:_GetPrefixFromGroup(SpawnGroup)
@ -2319,10 +2445,13 @@ end
-- @param Wrapper.Group#GROUP group Group to be despawned.
function RAT:_Despawn(group)
env.info("RAT debug _despawn 0")
local index=self:GetSpawnIndexFromGroup(group)
env.info("RAT debug index = "..index)
self.ratcraft[index].group:Destroy()
self.ratcraft[index].group=nil
env.info("RAT debug _despawn 1")
-- Decrease group alive counter.
self.alive=self.alive-1
@ -2330,7 +2459,7 @@ function RAT:_Despawn(group)
if self.f10menu then
self.Menu[self.SubMenuName]["groups"][index]:Remove()
end
env.info("RAT debug _despawn 2")
--TODO: Maybe here could be some more arrays deleted?
end
@ -2410,6 +2539,10 @@ function RAT:_Waypoint(Type, Coord, Speed, Altitude, Airport)
_Altitude = 0
_alttype="RADIO"
_AID = Airport:GetID()
elseif Type==RAT.wp.finalwp then
_Type="Turning Point"
_Action="Fly Over Point"
_alttype="BARO"
else
env.error("Unknown waypoint type in RAT:Waypoint() function!")
_Type="Turning Point"
@ -2478,9 +2611,6 @@ function RAT:_Waypoint(Type, Coord, Speed, Altitude, Airport)
--env.error(RAT.id.."Unknown Airport categoryin _Waypoint()!")
end
end
-- if _AID then
-- RoutePoint.airdromeId=_AID
-- end
-- properties
RoutePoint.properties = {
["vnav"] = 1,
@ -2494,6 +2624,16 @@ function RAT:_Waypoint(Type, Coord, Speed, Altitude, Airport)
-- Duration of holing. Between 10 and 170 seconds.
local Duration=self:_Randomize(90,0.9)
RoutePoint.task=self:_TaskHolding({x=Coord.x, y=Coord.z}, Altitude, Speed, Duration)
elseif Type==RAT.wp.finalwp then
local TaskRespawn, argkey, arg = self:_TaskFunction("RAT._FinalWaypoint", self)
self.argkey=argkey
self.arg=arg
local TaskCombo = {TaskRespawn}
RoutePoint.task = {}
RoutePoint.task.id = "ComboTask"
RoutePoint.task.params = {}
RoutePoint.task.params.tasks = TaskCombo
self:E(TaskRespawn)
else
RoutePoint.task = {}
RoutePoint.task.id = "ComboTask"
@ -2551,9 +2691,6 @@ function RAT:_Routeinfo(waypoints, comment)
return total
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Orbit at a specified position at a specified alititude with a specified speed.
@ -2607,6 +2744,68 @@ function RAT:_TaskHolding(P1, Altitude, Speed, Duration)
return DCSTask
end
--- Function called if aircraft reached its final waypoint. Aircraft gets destroyed and respawned.
-- @param Core.Group#GROUP group Group of aircraft.
-- @param #RAT rat RAT object.
function RAT._FinalWaypoint(group, rat)
env.info(RAT.id.."FinalWaypoint:")
BASE:E(group)
BASE:E(rat)
-- Spawn new group.
rat:_Respawn(group)
-- Despawn old group.
rat:_Despawn(group)
end
--- Orbit at a specified position at a specified alititude with a specified speed.
-- @param #RAT self
-- @param #string FunctionString Name of the function to be called.
function RAT:_TaskFunction(FunctionString, ... )
self:F2({FunctionString, arg})
local DCSTask
local ArgumentKey
local templatename=self.templategroup:GetName()
local groupname=self:_AnticipatedGroupName()
env.info(RAT.id.."template name "..templatename)
env.info(RAT.id.."anticipated name "..groupname)
local DCSScript = {}
--DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) "
DCSScript[#DCSScript+1] = "env.info(\"RAT blabla\") "
DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:FindByName(\""..groupname.."\") "
DCSScript[#DCSScript+1] = "local RATtemplateControllable = GROUP:FindByName(\""..templatename.."\") "
if arg and arg.n > 0 then
ArgumentKey = '_' .. tostring(arg):match("table: (.*)")
env.info(RAT.id.."Argumentkey: "..ArgumentKey)
self.templategroup:SetState(self.templategroup, ArgumentKey, arg)
DCSScript[#DCSScript+1] = "local Arguments = RATtemplateControllable:GetState(RATtemplateControllable, '" .. ArgumentKey .. "' ) "
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, unpack( Arguments ) )"
else
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )"
end
DCSTask = self.templategroup:TaskWrappedAction(self.templategroup:CommandDoScript(table.concat(DCSScript)))
env.info(RAT.id.."Taskfunction:")
self:E( DCSTask )
return DCSTask, ArgumentKey, arg
end
--- Anticipated group name from alias and spawn index.
-- @param #RAT self
-- @return #string Name the group will get after it is spawned.
function RAT:_AnticipatedGroupName()
return string.format("%s#%03d", self.alias, self.SpawnIndex+1)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Calculate the max flight level for a given distance and fixed climb and descent rates.
@ -2855,9 +3054,11 @@ function RAT:_PlaceMarkers(waypoints)
self:_SetMarker("Climb", waypoints[2])
self:_SetMarker("Begin of Cruise", waypoints[3])
self:_SetMarker("End of Cruise", waypoints[4])
self:_SetMarker("Descent", waypoints[5])
self:_SetMarker("Holding Point", waypoints[6])
self:_SetMarker("Destination", waypoints[7])
if #waypoints>4 then
self:_SetMarker("Descent", waypoints[5])
self:_SetMarker("Holding Point", waypoints[6])
self:_SetMarker("Destination", waypoints[7])
end
end
@ -2949,7 +3150,8 @@ function RAT:_ModifySpawnTemplate(waypoints)
-- Parking spot.
UnitTemplate.parking = nil
UnitTemplate.parking_id = nil
UnitTemplate.parking_id = self.parking_id
--env.info(RAT.id.."Parking ID "..tostring(self.parking_id))
-- Initial altitude
UnitTemplate.alt=PointVec3.y
@ -2969,7 +3171,7 @@ function RAT:_ModifySpawnTemplate(waypoints)
--SpawnTemplate.uncontrolled=true
-- Update modified template for spawn group.
self.SpawnGroups[self.SpawnIndex].SpawnTemplate=SpawnTemplate
--self.SpawnGroups[self.SpawnIndex].SpawnTemplate=SpawnTemplate
self:T(SpawnTemplate)
end

View File

@ -36,11 +36,14 @@
-- @field #number Tsuppress_min Minimum time in seconds the group gets suppressed.
-- @field #number Tsuppress_max Maximum time in seconds the group gets suppressed.
-- @field #number life Relative life in precent of the group.
-- @field #number Tsuppress Time in seconds the groups is suppressed. Randomly chosen between Tsuppress_min and Tsuppress_max.
-- @field #number TsuppressionStart Time at which the suppression started.
-- @field #number TsuppressionOver Time at which the suppression will be over.
-- @field #number Thit Last time the unit was hit.
-- @field #number Nhit Number of times the unit was hit since it last was in state "CombatReady".
-- @field Core.Zone#ZONE Zone_Retreat Zone into which a group retreats.
-- @field #number LifeMin Life of group in percent at which the group will be ordered to retreat.
-- @field #number LifeThreshold Life of group in percent at which the group will be ordered to retreat.
-- @field #number IniGroupStrength Number of units in a group at start.
-- @field #number GroupStrengthThreshold Threshold of group strength before retreat is ordered.
-- @extends Core.Fsm#FSM_CONTROLLABLE
--
@ -54,11 +57,14 @@ AI_Suppression={
ClassName = "AI_Suppression",
Tsuppress_min = 5,
Tsuppress_max = 20,
Tsuppress = nil,
TsuppressStart = nil,
TsuppressOver = nil,
Thit = nil,
Nhit = 0,
Zone_Retreat = nil,
LifeMin = 25,
LifeThreshold = 25,
IniGroupStrength = nil,
GroupStrengthThreshold=80,
}
--- Some ID to identify who we are in output of the DCS.log file.
@ -67,7 +73,7 @@ AI_Suppression.id="SFX | "
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--TODO: Figure out who was shooting and move away from him <== Not possible!
--TODO: Figure out who was shooting and move away from him.
--TODO: Move behind a scenery building if there is one nearby.
--TODO: Retreat to a given zone or point.
@ -81,38 +87,52 @@ function AI_Suppression:New(Group)
-- Check that group is present.
if Group then
env.info("Suppression fire for group "..Group:GetName())
env.info(AI_Suppression.id.."Suppression fire for group "..Group:GetName())
else
env.info("Suppression fire: Group does not exist!")
env.info(AI_Suppression.id.."Suppression fire: Requested group does not exist! (Has to be a MOOSE group.)")
return nil
end
-- Check that we actually have a GROUND group.
if Group:IsGround()==false then
env.error("Suppression fire group "..Group:GetName().." has to be a GROUND group!")
env.error(AI_Suppression.id.."Suppression fire group "..Group:GetName().." has to be a GROUND group!")
return nil
end
-- Inherits from FSM_CONTROLLABLE
local self=BASE:Inherit(self, FSM_CONTROLLABLE:New()) -- #AI_Suppression
-- Set the controllable for the FSM.
self:SetControllable(Group)
-- Initial group strength.
self.IniGroupStrength=#Group:GetUnits()
-- Get life of group in %.
local life_min, life_max, life_ave, groupstrength=self:_GetLife()
-- Group is initially in state CombatReady.
self:SetStartState("CombatReady")
self:SetStartState("none")
-- Transitions:
---------------
-- Transition from anything to "Suppressed" after event "Hit".
self:AddTransition("*", "Start", "CombatReady")
-- Transition from anything to "Suppressed" after event "Hit".
self:AddTransition("*", "Hit", "Suppressed")
self:AddTransition("*", "Hit", "*")
-- Transition from "Suppressed" back to "CombatReady after the unit had time to recover.
self:AddTransition("Suppressed", "Recovered", "CombatReady")
self:AddTransition("*", "Recovered", "*")
-- Transition from "Suppressed" back to "CombatReady after the unit had time to recover.
self:AddTransition("*", "Suppress", "Suppressed")
-- Transition from "Suppressed" to "Hiding" after event "Hit".
self:AddTransition("Suppressed", "TakeCover", "Hiding")
self:AddTransition("*", "TakeCover", "Hiding")
-- Transition from anything to "Retreating" after e.g. being severely damaged.
self:AddTransition("*", "Retreat", "Retreating")
@ -123,12 +143,7 @@ function AI_Suppression:New(Group)
-- Check status of the group.
self:AddTransition("*", "Status", "*")
-- Handle DCS event hit.
self:HandleEvent(EVENTS.Hit, self.OnEventHit)
-- Handle DCS event dead.
self:HandleEvent(EVENTS.Dead, self.OnEventDead)
--self:TakeCover()
-- return self
return self
@ -158,6 +173,21 @@ end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- After "Start" event.
-- @param #AI_Suppression self
function AI_Suppression:onafterStart(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("onafterStart: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Handle DCS event hit.
self:HandleEvent(EVENTS.Hit, self._OnHit)
-- Handle DCS event dead.
self:HandleEvent(EVENTS.Dead, self._OnDead)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Status" event.
-- @param #AI_Suppression self
function AI_Suppression:OnBeforeStatus(Controlable, From, Event, To)
@ -174,10 +204,16 @@ function AI_Suppression:OnAfterStatus(Controlable, From, Event, To)
self:__Status(30)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Hit" event. (Of course, this is not really before the group got hit.)
-- @param #AI_Suppression self
function AI_Suppression:OnBeforeHit(Controlable, From, Event, To)
-- @param Wrapper.Controllable#CONTROLLABLE Controlable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Fallback Fallback coordinates (or nil if no attacker could be found).
function AI_Suppression:OnBeforeHit(Controlable, From, Event, To, Fallback)
env.info(AI_Suppression.id..string.format("OnBeforeHit: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Increase Hit counter.
@ -185,16 +221,51 @@ function AI_Suppression:OnBeforeHit(Controlable, From, Event, To)
-- Info on hit times.
env.info(AI_Suppression.id..string.format("Group has just been hit %d times.", self.Nhit))
end
--- After "Hit" event.
-- @param #AI_Suppression self
function AI_Suppression:OnAfterHit(Controlable, From, Event, To)
function AI_Suppression:OnAfterHit(Controlable, From, Event, To, Fallback)
env.info(AI_Suppression.id..string.format("OnAfterHit: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Nothing to do yet. Just monitoring the event.
-- Suppress fire of group.
self:_Suppress()
-- Get life of group in %.
local life_min, life_max, life_ave, groupstrength=self:_GetLife()
if self:is("CombatReady") then
env.info(AI_Suppression.id..string.format("Group %s is currently CombatReady.", Controlable:GetName()))
self:Suppress()
elseif self:Is("Suppressed") then
env.info(AI_Suppression.id..string.format("Group %s is currently Suppressed.", Controlable:GetName()))
elseif self:Is("Retreating") then
env.info(AI_Suppression.id..string.format("Group %s is currently Retreating.", Controlable:GetName()))
elseif self:is("Hiding") then
env.info(AI_Suppression.id..string.format("Group %s is currently Hiding.", Controlable:GetName()))
end
-- After three hits fall back a bit.
local nfallback=3
if self.Nhit==nfallback then
env.info(AI_Suppression.id..string.format("Group %s is falling back after %d hits.", Controlable:GetName(), nfallback))
Fallback:SmokeGreen()
local FallbackMarkerID=Fallback:MarkToAll("Fall back position for group "..Controlable:GetName():GetName())
self:_FallBack(Fallback)
end
-- If life of one unit is below threshold, the group is ordered to retreat (if a zone has been specified).
if not self:Is("Retreating") then
if groupstrength<self.GroupStrengthThreshold or (self.IniGroupStrength==1 and life_min < self.LifeThreshold) then
self.Controllable:ClearTasks()
self:Retreat()
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Recovered" event.
-- @param #AI_Suppression self
@ -204,12 +275,7 @@ function AI_Suppression:OnBeforeRecovered(Controlable, From, Event, To)
-- Current time.
local Tnow=timer.getTime()
-- Here I need to figure our how to correctly go back to "CombatReady".
-- Problem is that another "Hit" event might occur while the group is recovering.
-- If that happens the time to recover should be reset.
-- Only after a unit has not been hit for X seconds.
-- We can return false if the recovery should not be executed!
-- Debug info
env.info(AI_Suppression.id..string.format("OnBeforeRecovered: Time: %d - Time over: %d", Tnow, self.TsuppressionOver))
-- Recovery is only possible if enough time since the last hit has passed.
@ -232,6 +298,7 @@ function AI_Suppression:OnAfterRecovered(Controlable, From, Event, To)
-- Nothing to do yet. Just monitoring the event.
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Retreat" event.
-- @param #AI_Suppression self
@ -240,10 +307,13 @@ function AI_Suppression:OnBeforeRetreat(Controlable, From, Event, To)
-- Retreat is only possible if a zone has been defined by the user.
if self.Zone_Retreat==nil then
env.info("SFX: Retreat NOT possible! No Zone specified.")
env.info(AI_Suppression.id.."Retreat NOT possible! No Zone specified.")
return false
elseif self:Is("Retreating") then
env.info(AI_Suppression.id.."Group is already retreating.")
return false
else
env.info("SFX: Retreat possible, zone specified.")
env.info(AI_Suppression.id.."Retreat possible, zone specified.")
return true
end
@ -254,11 +324,17 @@ end
function AI_Suppression:OnAfterRetreat(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnAfterRetreat: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Message.
local text=string.format("Group %s is retreating to zone %s.", Controlable:GetName(), self.Zone_Retreat:GetName())
MESSAGE:New(text, 30):ToAll()
-- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire.
Controlable:OptionAlarmStateGreen()
-- Route the group to a zone.
MESSAGE:New(string.format("Group %s is retreating!", Controlable:GetName()), 30):ToAll()
self:_RetreatToZone(self.Zone_Retreat, 50, "Vee")
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "TakeCover" event.
-- @param #AI_Suppression self
function AI_Suppression:OnBeforeTakeCover(Controlable, From, Event, To)
@ -266,16 +342,18 @@ function AI_Suppression:OnBeforeTakeCover(Controlable, From, Event, To)
-- We search objects in a zone with radius 100 m around the group.
-- TODO: Maybe make the zone radius larger for vehicles.
local Zone = ZONE_GROUP:New("Zone_Hiding", Controlable, 100)
local Zone = ZONE_GROUP:New("Zone_Hiding", Controlable, 500)
-- Scan for Scenery objects to run/drive to.
Zone:Scan( Object.Category.SCENERY )
Zone:Scan(Object.Category.SCENERY)
local gothideout=false
for SceneryTypeName, SceneryData in pairs( Zone:GetScannedScenery() ) do
for SceneryName, SceneryObject in pairs( SceneryData ) do
local SceneryObject = SceneryObject -- Wrapper.Scenery#SCENERY
MESSAGE:NewType( "Scenery: " .. SceneryObject:GetTypeName() .. ", Coord LL DMS: " .. SceneryObject:GetCoordinate():ToStringLLDMS(), MESSAGE.Type.Information ):ToAll()
local text=self.Controllable:GetName().. " scenery: " .. SceneryObject:GetTypeName() .. ", Coord LL DMS: " .. SceneryObject:GetCoordinate():ToStringLLDMS()
MESSAGE:New(text, 10):ToAll()
env.info(AI_Suppression.id..text)
-- TODO: Add check if scenery name matches a specific type like tree or building. This might be tricky though!
end
end
@ -287,77 +365,69 @@ end
--- After "TakeCover" event.
-- @param #AI_Suppression self
function AI_Suppression:OnBeforeTakeCover(Controlable, From, Event, To)
function AI_Suppression:OnAfterTakeCover(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnAfterTakeCover: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
local text=string.format("Group %s is taking cover!", Controlable:GetName())
MESSAGE:New(text, 30):ToAll()
-- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire.
Controlable:OptionAlarmStateGreen()
-- Route the group to a zone.
MESSAGE:New(string.format("Group %s would be(!) hiding now!", Controlable:GetName()), 30):ToAll()
--TODO: Search place to hide. For each unit (disperse) or same for all?
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Entering "CombatReady" state. The group will be able to fight back.
-- @param #AI_Suppression self
function AI_Suppression:OnEnterCombatReady(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnEnterCombatReady: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Group can fight again.
self.Controllable:OptionROEOpenFire()
-- Nothing to do yet. Just monitoring the event
end
--- Leaving "CombatReady" state.
-- @param #AI_Suppression self
function AI_Suppression:OnLeaveCombatReady(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnLeaveCombatReady: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Nothing to do yet. Just monitoring the event
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Entering "Suppressed" state. Group will not fight but hold their weapons.
-- @param #AI_Suppression self
function AI_Suppression:OnEnterSuppressed(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnEnterSuppression: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Nothing to do yet. Just monitoring the event
end
-- Current time.
local Tnow=timer.getTime()
--- Leaving "Suppressed" state.
-- @param #AI_Suppression self
function AI_Suppression:OnLeaveSuppressed(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnLeaveSuppression: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Group will hold their weapons.
Controlable:OptionROEHoldFire()
-- Group can fight again.
self.Controllable:OptionROEOpenFire()
-- Get randomized time the unit is suppressed.
self.Tsuppress=math.random(self.Tsuppress_min, self.Tsuppress_max)
-- Time at which the suppression is over.
self.TsuppressionOver=Tnow+self.Tsuppress
-- Recovery event will be called in Tsuppress seconds. (We add one second to be sure the time has really passed when recovery is checked.)
self:__Recovered(self.Tsuppress+1)
-- Get life of group in %.
local life_min, life_max, life_ave=self:_GetLife()
-- If life of one unit is below threshold, the group is ordered to retreat (if a zone has been specified).
if life_min < self.LifeMin then
self:Retreat()
end
local text=string.format("Suppression of group %s ended at %f and should have ended at %f.", self.Controllable:GetName(), timer.getTime(), self.TsuppressionOver)
env.info(AI_Suppression.id..text)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Entering "Retreating" state. Group will be send to a zone.
-- @param #AI_Suppression self
-- @param Wrapper.Controllable#CONTROLLABLE Controlable Controllable of the AI group.
function AI_Suppression:OnEnterRetreating(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnEnterRetreating: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire.
Controlable:OptionAlarmStateGreen()
-- Route the group to a zone.
MESSAGE:New(string.format("Group %s is retreating!", Controlable:GetName()), 30):ToAll()
self:_RetreatToZone(self.Zone_Retreat, 50, "Vee")
end
--- Leaving "Retreating" state.
@ -370,21 +440,13 @@ function AI_Suppression:OnLeaveRetreating(Controlable, From, Event, To)
Controlable:OptionAlarmStateAuto()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Entering "Hiding" state. Group will try to take cover at neargy scenery objects.
-- @param #AI_Suppression self
-- @param Wrapper.Controllable#CONTROLLABLE Controlable Controllable of the AI group.
function AI_Suppression:OnEnterHiding(Controlable, From, Event, To)
env.info(AI_Suppression.id..string.format("OnEnterHiding: %s event %s from %s to %s", Controlable:GetName(), Event, From, To))
-- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire.
Controlable:OptionAlarmStateGreen()
-- Route the group to a zone.
MESSAGE:New(string.format("Group %s would be(!) hiding now!", Controlable:GetName()), 30):ToAll()
--TODO: Search place to hide. For each unit (disperse) or same for all?
end
--- Leaving "Hiding" state.
@ -397,35 +459,128 @@ function AI_Suppression:OnLeaveHiding(Controlable, From, Event, To)
Controlable:OptionAlarmStateAuto()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Handle the DCS event hit.
-- @param #AI_Suppression self
-- @param Core.Event#EVENTDATA EventData
function AI_Suppression:OnEventHit(EventData)
self:E({"EventHit", EventData })
env.info("Hitevent")
function AI_Suppression:_OnHit(EventData)
self:E( {AI_Suppression.id.."_OnHit", EventData })
--env.info(AI_Suppression.id.."Initiator : "..EventData.IniDCSGroupName)
--env.info(AI_Suppression.id.."Target : "..EventData.TgtDCSGroupName)
--env.info(AI_Suppression.id.."Controllable: "..self.Controllable:GetName())
if EventData.IniDCSUnit then
-- Call "Hit" event.
self:Hit()
if EventData.TgtDCSGroup then
local TargetGroup=EventData.TgtGroup --Wrapper.Group#GROUP
if EventData.TgtDCSGroupName==self.Controllable:GetName() then
-- Figure out who shot.
local InitiatorName="unknown"
local Fallback=nil
if EventData.IniDCSUnit then
local InitiatorUnit=EventData.IniUnit --Wrapper.Unit#UNIT
InitiatorName=EventData.IniDCSGroupName
local TC=TargetGroup:GetCoordinate()
local IC=InitiatorUnit:GetCoordinate()
-- Create a fall back point.
Fallback=self:_FallBackCoord(TC, IC , 200) -- Core.Point#COORDINATE
end
-- Get life of group in %.
local life_min, life_max, life_ave, groupstrength=self:_GetLife()
-- Debug message.
local text=string.format("Group %s was hit by %s. Life min=%02d %%, max=%02d %%, average=%02d %%, group=%3.0f", EventData.TgtDCSGroupName, InitiatorName, life_min, life_max, life_ave, groupstrength)
MESSAGE:New(text, 10):ToAll()
env.info(AI_Suppression.id..text)
-- Trigger Hit event.
self:Hit(Fallback)
end
end
end
--- Handle the DCS event dead.
-- @param #AI_Suppression self
-- @param Core.Event#EVENTDATA EventData
function AI_Suppression:OnEventDead(EventData)
self:E({"EventDead", EventData })
env.info("Deadevent")
function AI_Suppression:_OnDead(EventData)
self:E({AI_Suppression.id.."_OnDead", EventData})
if EventData.IniDCSUnit then
--self:Died()
if EventData.IniDCSGroupName==self.Controllable:GetName() then
-- Number of units left in the group.
local nunits=#self.Controllable:GetUnits()-1
local text=string.format("A unit from group %s just died! %d units left.", self.Controllable:GetName(), nunits)
MESSAGE:New(text, 10):ToAll()
env.info(AI_Suppression.id..text)
-- Go to stop state.
if nunits==0 then
self:Stop()
end
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Suppress fire of a unit.
-- @param #AI_Suppression self
function AI_Suppression:_Suppress()
-- Current time.
local Tnow=timer.getTime()
-- Controllable
local Controlable=self.Controllable
-- Group will hold their weapons.
Controlable:OptionROEHoldFire()
-- Get randomized time the unit is suppressed.
local Tsuppress=math.random(self.Tsuppress_min, self.Tsuppress_max)
-- Time the suppression started
self.TsuppressionStart=Tnow
-- Time at which the suppression is over.
local renew=true
if self.TsuppressionOver~=nil then
if Tsuppress+Tnow>self.TsuppressionOver then
self.TsuppressionOver=Tnow+Tsuppress
else
renew=false
end
else
self.TsuppressionOver=Tnow+Tsuppress
end
-- Recovery event will be called in Tsuppress seconds. (We add one second to be sure the time has really passed when recovery is checked.)
if renew then
self:__Recovered(self.TsuppressionOver-Tnow)
end
-- Debug message.
local text=string.format("Group %s is suppressed for %d seconds.", Controlable:GetName(), Tsuppress)
MESSAGE:New(text, 30):ToAll()
env.info(AI_Suppression.id..text)
text=string.format("Suppression starts at %f and ends at %f.", Tnow, self.TsuppressionOver)
env.info(AI_Suppression.id..text)
end
--- Get (relative) life in percent of a group. Function returns the value of the units with the smallest and largest life. Also the average value of all groups is returned.
-- @param #AI_Suppression self
-- @param Wrapper.Group#GROUP group Group of unit.
@ -440,10 +595,12 @@ function AI_Suppression:_GetLife()
local life_ave=0
local n=0
local units=group:GetUnits()
local groupstrength=#units/self.IniGroupStrength*100
for _,unit in pairs(units) do
if unit then
local unit=unit -- Wrapper.Unit#UNIT
if unit and unit:IsActive() then
n=n+1
local life=unit:GetLife()/unit:GetLife0()*100
local life=unit:GetLife()/(unit:GetLife0()+1)*100
if life < life_min then
life_min=life
end
@ -451,10 +608,13 @@ function AI_Suppression:_GetLife()
life_max=life
end
life_ave=life_ave+life
local text=string.format("n=%d: Life = %3.1f, Life0 = %3.1f, min=%3.1f, max=%3.1f, ave=%3.1f, group=%3.1f", n, unit:GetLife(), unit:GetLife0(), life_min, life_max, life_ave/n,groupstrength)
env.info(AI_Suppression.id..text)
end
end
life_ave=life_ave/n
return life_min, life_max, life_ave
return life_min, life_max, life_ave, groupstrength
else
return 0, 0, 0
end
@ -473,12 +633,102 @@ function AI_Suppression:_RetreatToZone(zone, speed, formation)
speed = speed or 999
formation = formation or "Vee"
--
env.info(AI_Suppression.id.."Retreat zone : "..zone:GetName())
-- Get a random point in the retreat zone.
local ZonePoint=zone:GetRandomPointVec2()
local ZoneCoord=zone:GetRandomCoordinate() -- Core.Point#COORDINATE
local ZoneVec2=ZoneCoord:GetVec2()
-- Debug smoke zone and point.
ZoneCoord:SmokeBlue()
zone:SmokeZone(SMOKECOLOR.Red, 12)
-- Set task to go to zone.
self.Controllable:TaskRouteToVec2(ZonePoint, speed, formation)
self.Controllable:TaskRouteToVec2(ZoneVec2, speed, formation)
end
--- Determine the coordinate to which a unit should fall back.
--@param #AI_Suppression self
--@param Core.Point#COORDINATE a Coordinate of the defending group.
--@param Core.Point#COORDINATE b Coordinate of the attacking group.
--@return Core.Point#COORDINATE Fallback coordinates.
function AI_Suppression:_FallBackCoord(a, b, distance)
local dx = b.x-a.x
-- take the right value for y-coordinate (if we have "alt" then "y" if not "z")
local ay
if a.alt then
ay=a.y
else
ay=a.z
end
local by
if b.alt then
by=b.y
else
by=b.z
end
local dy = by-ay
local angle = math.deg(math.atan2(dy,dx))
if angle < 0 then
angle = 360 + angle
end
angle=angle-180
local fbp=a:Translate(distance, angle)
return fbp
end
--- Fall back (move away) from enemy who is shooting on the group.
--@param #AI_Suppression self
--@param Core.Point#COORDINATE coord_fbp Coordinate of the fall back point.
function AI_Suppression:_FallBack(coord_fbp)
local group=self.Controllable -- Wrapper.Controllable#CONTROLLABLE
local Waypoints = group:GetTemplateRoutePoints()
local coord_grp = group:GetCoordinate()
local wp1 = coord_grp:WaypointGround(99, "Vee")
local wp2 = coord_fbp:WaypointGround(99, "Vee")
table.insert(Waypoints, 1, wp1)
table.insert(Waypoints, 2, wp2)
-- Condition to wait.
local ConditionWait=group:TaskCondition(nil, nil, nil, nil, 30, nil)
-- Task to hold.
local TaskHold = group:TaskHold()
local TaskRoute1 = group:TaskFunction("AI_Suppression._Passing_Waypoint", self, 0)
local TaskCombo2 = {}
TaskCombo2[#TaskCombo2+1] = group:TaskFunction("AI_Suppression._Passing_Waypoint", self, 1)
TaskCombo2[#TaskCombo2+1] = group:TaskControlled(TaskHold, ConditionWait)
local TaskRoute2 = group:TaskCombo(TaskCombo2)
group:SetTaskWaypoint(Waypoints[1], TaskRoute1)
group:SetTaskWaypoint(Waypoints[2], TaskRoute2)
group:Route(Waypoints)
end
--- Group has reached a waypoint.
--@param #AI_Suppression self
--@param #number i Waypoint number that has been reached.
function AI_Suppression._Passing_Waypoint(group, Fsm, i)
env.info(AI_Suppression.id.."Passing waypoint")
BASE:E(group)
BASE:E(Fsm)
BASE:E(i)
MESSAGE:New(string.format("Group %s passing waypoint %d", group:GetName(), i),30):ToAll()
if i==1 then
MESSAGE:New(string.format("Group %s has reached fallback point.", group:GetName(), i),30):ToAll()
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------