Frank 389931fbf0 AI_PATROL
- output fix
2019-12-21 01:09:59 +01:00

1991 lines
77 KiB
Lua

--- **Functional** - Suppress fire of ground units when they get hit.
--
-- ===
--
-- ## Features:
--
-- * Hold fire of attacked units when being fired upon.
-- * Retreat to a user defined zone.
-- * Fall back on hits.
-- * Take cover on hits.
-- * Gaussian distribution of suppression time.
--
-- ===
--
-- ## Missions:
--
-- ## [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
--
-- ===
--
-- When ground units get hit by (suppressive) enemy fire, they will not be able to shoot back for a certain amount of time.
--
-- The implementation is based on an idea and script by MBot. See the [DCS forum threat](https://forums.eagle.ru/showthread.php?t=107635) for details.
--
-- In addition to suppressing the fire, conditions can be specified which let the group retreat to a defined zone, move away from the attacker
-- or hide at a nearby scenery object.
--
-- ====
--
-- # YouTube Channel
--
-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg)
--
-- ===
--
-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)**
--
-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536)
--
-- ===
--
-- @module Functional.Suppression
-- @image Suppression.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- SUPPRESSION class
-- @type SUPPRESSION
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players.
-- @field #string lid String for DCS log file.
-- @field #boolean flare Flare units when they get hit or die.
-- @field #boolean smoke Smoke places to which the group retreats, falls back or hides.
-- @field #list DCSdesc Table containing all DCS descriptors of the group.
-- @field #string Type Type of the group.
-- @field #number SpeedMax Maximum speed of group in km/h.
-- @field #boolean IsInfantry True if group has attribute Infantry.
-- @field Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the FSM. Must be a ground group.
-- @field #number Tsuppress_ave Average time in seconds a group gets suppressed. Actual value is sampled randomly from a Gaussian distribution.
-- @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 TsuppressionOver Time at which the suppression will be over.
-- @field #number IniGroupStrength Number of units in a group at start.
-- @field #number Nhit Number of times the group was hit.
-- @field #string Formation Formation which will be used when falling back, taking cover or retreating. Default "Vee".
-- @field #number Speed Speed the unit will use when falling back, taking cover or retreating. Default 999.
-- @field #boolean MenuON If true creates a entry in the F10 menu.
-- @field #boolean FallbackON If true, group can fall back, i.e. move away from the attacking unit.
-- @field #number FallbackWait Time in seconds the unit will wait at the fall back point before it resumes its mission.
-- @field #number FallbackDist Distance in meters the unit will fall back.
-- @field #number FallbackHeading Heading in degrees to which the group should fall back. Default is directly away from the attacking unit.
-- @field #boolean TakecoverON If true, group can hide at a nearby scenery object.
-- @field #number TakecoverWait Time in seconds the group will hide before it will resume its mission.
-- @field #number TakecoverRange Range in which the group will search for scenery objects to hide at.
-- @field Core.Point#COORDINATE hideout Coordinate/place where the group will try to take cover.
-- @field #number PminFlee Minimum probability in percent that a group will flee (fall back or take cover) at each hit event. Default is 10 %.
-- @field #number PmaxFlee Maximum probability in percent that a group will flee (fall back or take cover) at each hit event. Default is 90 %.
-- @field Core.Zone#ZONE RetreatZone Zone to which a group retreats.
-- @field #number RetreatDamage Damage in percent at which the group will be ordered to retreat.
-- @field #number RetreatWait Time in seconds the group will wait in the retreat zone before it resumes its mission. Default two hours.
-- @field #string CurrentAlarmState Alam state the group is currently in.
-- @field #string CurrentROE ROE the group currently has.
-- @field #string DefaultAlarmState Alarm state the group will go to when it is changed back from another state. Default is "Auto".
-- @field #string DefaultROE ROE the group will get once suppression is over. Default is "Free".
-- @field #boolean eventmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. Default true.
-- @extends Core.Fsm#FSM_CONTROLLABLE
--
--- Mimic suppressive enemy fire and let groups flee or retreat.
--
-- ## Suppression Process
--
-- ![Process](..\Presentations\SUPPRESSION\Suppression_Process.png)
--
-- The suppression process can be described as follows.
--
-- ### CombatReady
--
-- A group starts in the state **CombatReady**. In this state the group is ready to fight. The ROE is set to either "Weapon Free" or "Return Fire".
-- The alarm state is set to either "Auto" or "Red".
--
-- ### Event Hit
-- The most important event in this scenario is the **Hit** event. This is an event of the FSM and triggered by the DCS event hit.
--
-- ### Suppressed
-- After the **Hit** event the group changes its state to **Suppressed**. Technically, the ROE of the group is changed to "Weapon Hold".
-- The suppression of the group will last a certain amount of time. It is randomized an will vary each time the group is hit.
-- The expected suppression time is set to 15 seconds by default. But the actual value is sampled from a Gaussian distribution.
--
-- ![Process](..\Presentations\SUPPRESSION\Suppression_Gaussian.png)
--
-- The graph shows the distribution of suppression times if a group would be hit 100,000 times. As can be seen, on most hits the group gets
-- suppressed for around 15 seconds. Other values are also possible but they become less likely the further away from the "expected" suppression time they are.
-- Minimal and maximal suppression times can also be specified. By default these are set to 5 and 25 seconds, respectively. This can also be seen in the graph
-- because the tails of the Gaussian distribution are cut off at these values.
--
-- ### Event Recovered
-- After the suppression time is over, the event **Recovered** is initiated and the group becomes **CombatReady** again.
-- The ROE of the group will be set to "Weapon Free".
--
-- Of course, it can also happen that a group is hit again while it is still suppressed. In that case a new random suppression time is calculated.
-- If the new suppression time is longer than the remaining suppression of the previous hit, then the group recovers when the suppression time of the last
-- hit has passed.
-- If the new suppression time is shorter than the remaining suppression, the group will recover after the longer time of the first suppression has passed.
--
-- For example:
--
-- * A group gets hit the first time and is suppressed for - let's say - 15 seconds.
-- * After 10 seconds, i.e. when 5 seconds of the old suppression are left, the group gets hit a again.
-- * A new suppression time is calculated which can be smaller or larger than the remaining 5 seconds.
-- * If the new suppression time is smaller, e.g. three seconds, than five seconds, the group will recover after the 5 remaining seconds of the first suppression have passed.
-- * If the new suppression time is longer than last suppression time, e.g. 10 seconds, then the group will recover after the 10 seconds of the new hit have passed.
--
-- Generally speaking, the suppression times are not just added on top of each other. Because this could easily lead to the situation that a group
-- never becomes CombatReady again before it gets destroyed.
--
-- The mission designer can capture the event **Recovered** by the function @{#SUPPRESSION.OnAfterRecovered}().
--
-- ## Flee Events and States
-- Apart from being suppressed the groups can also flee from the enemy under certain conditions.
--
-- ### Event Retreat
-- The first option is a retreat. This can be enabled by setting a retreat zone, i.e. a trigger zone defined in the mission editor.
--
-- If the group takes a certain amount of damage, the event **Retreat** will be called and the group will start to move to the retreat zone.
-- The group will be in the state **Retreating**, which means that its ROE is set to "Weapon Hold" and the alarm state is set to "Green".
-- Setting the alarm state to green is necessary to enable the group to move under fire.
--
-- When the group has reached the retreat zone, the event **Retreated** is triggered and the state will change to **Retreated** (note that both the event and
-- the state of the same name in this case). ROE and alarm state are
-- set to "Return Fire" and "Auto", respectively. The group will stay in the retreat zone and not actively participate in the combat any more.
--
-- If no option retreat zone has been specified, the option retreat is not available.
--
-- The mission designer can capture the events **Retreat** and **Retreated** by the functions @{#SUPPRESSION.OnAfterRetreat}() and @{#SUPPRESSION.OnAfterRetreated}().
--
-- ### Fallback
--
-- If a group is attacked by another ground group, it has the option to fall back, i.e. move away from the enemy. The probability of the event **FallBack** to
-- happen depends on the damage of the group that was hit. The more a group gets damaged, the more likely **FallBack** event becomes.
--
-- If the group enters the state **FallingBack** it will move 100 meters in the opposite direction of the attacking unit. ROE and alarmstate are set to "Weapon Hold"
-- and "Green", respectively.
--
-- At the fallback point the group will wait for 60 seconds before it resumes its normal mission.
--
-- The mission designer can capture the event **FallBack** by the function @{#SUPPRESSION.OnAfterFallBack}().
--
-- ### TakeCover
--
-- If a group is hit by either another ground or air unit, it has the option to "take cover" or "hide". This means that the group will move to a random
-- scenery object in it vicinity.
--
-- Analogously to the fall back case, the probability of a **TakeCover** event to occur, depends on the damage of the group. The more a group is damaged, the more
-- likely it becomes that a group takes cover.
--
-- When a **TakeCover** event occurs an area with a radius of 300 meters around the hit group is searched for an arbitrary scenery object.
-- If at least one scenery object is found, the group will move there. One it has reached its "hideout", it will wait there for two minutes before it resumes its
-- normal mission.
--
-- If more than one scenery object is found, the group will move to a random one.
-- If no scenery object is near the group the **TakeCover** event is rejected and the group will not move.
--
-- The mission designer can capture the event **TakeCover** by the function @{#SUPPRESSION.OnAfterTakeCover}().
--
-- ### Choice of FallBack or TakeCover if both are enabled?
--
-- If both **FallBack** and **TakeCover** events are enabled by the functions @{#SUPPRESSION.Fallback}() and @{#SUPPRESSION.Takecover}() the algorithm does the following:
--
-- * If the attacking unit is a ground unit, then the **FallBack** event is executed.
-- * Otherwise, i.e. if the attacker is *not* a ground unit, then the **TakeCover** event is triggered.
--
-- ### FightBack
--
-- When a group leaves the states **TakingCover** or **FallingBack** the event **FightBack** is triggered. This changes the ROE and the alarm state back to their default values.
--
-- The mission designer can capture the event **FightBack** by the function @{#SUPPRESSION.OnAfterFightBack}()
--
-- # Examples
--
-- ## Simple Suppression
-- This example shows the basic steps to use suppressive fire for a group.
--
-- ![Process](..\Presentations\SUPPRESSION\Suppression_Example_01.png)
--
--
-- # Customization and Fine Tuning
-- The following user functions can be used to change the default values
--
-- * @{#SUPPRESSION.SetSuppressionTime}() can be used to set the time a goup gets suppressed.
-- * @{#SUPPRESSION.SetRetreatZone}() sets the retreat zone and enables the possiblity for the group to retreat.
-- * @{#SUPPRESSION.SetFallbackDistance}() sets a value how far the unit moves away from the attacker after the fallback event.
-- * @{#SUPPRESSION.SetFallbackWait}() sets the time after which the group resumes its mission after a FallBack event.
-- * @{#SUPPRESSION.SetTakecoverWait}() sets the time after which the group resumes its mission after a TakeCover event.
-- * @{#SUPPRESSION.SetTakecoverRange}() sets the radius in which hideouts are searched.
-- * @{#SUPPRESSION.SetTakecoverPlace}() explicitly sets the place where the group will run at a TakeCover event.
-- * @{#SUPPRESSION.SetMinimumFleeProbability}() sets the minimum probability that a group flees (FallBack or TakeCover) after a hit. Note taht the probability increases with damage.
-- * @{#SUPPRESSION.SetMaximumFleeProbability}() sets the maximum probability that a group flees (FallBack or TakeCover) after a hit. Default is 90%.
-- * @{#SUPPRESSION.SetRetreatDamage}() sets the damage a group/unit can take before it is ordered to retreat.
-- * @{#SUPPRESSION.SetRetreatWait}() sets the time a group waits in the retreat zone after a retreat.
-- * @{#SUPPRESSION.SetDefaultAlarmState}() sets the alarm state a group gets after it becomes CombatReady again.
-- * @{#SUPPRESSION.SetDefaultROE}() set the rules of engagement a group gets after it becomes CombatReady again.
-- * @{#SUPPRESSION.FlareOn}() is mainly for debugging. A flare is fired when a unit is hit, gets suppressed, recovers, dies.
-- * @{#SUPPRESSION.SmokeOn}() is mainly for debugging. Puts smoke on retreat zone, hideouts etc.
-- * @{#SUPPRESSION.MenuON}() is mainly for debugging. Activates a radio menu item where certain functions like retreat etc. can be triggered manually.
--
--
-- @field #SUPPRESSION
SUPPRESSION={
ClassName = "SUPPRESSION",
Debug = false,
lid = nil,
flare = false,
smoke = false,
DCSdesc = nil,
Type = nil,
IsInfantry = nil,
SpeedMax = nil,
Tsuppress_ave = 15,
Tsuppress_min = 5,
Tsuppress_max = 25,
TsuppressOver = nil,
IniGroupStrength = nil,
Nhit = 0,
Formation = "Off road",
Speed = 4,
MenuON = false,
FallbackON = false,
FallbackWait = 60,
FallbackDist = 100,
FallbackHeading = nil,
TakecoverON = false,
TakecoverWait = 120,
TakecoverRange = 300,
hideout = nil,
PminFlee = 10,
PmaxFlee = 90,
RetreatZone = nil,
RetreatDamage = nil,
RetreatWait = 7200,
CurrentAlarmState = "unknown",
CurrentROE = "unknown",
DefaultAlarmState = "Auto",
DefaultROE = "Weapon Free",
eventmoose = true,
}
--- Enumerator of possible rules of engagement.
-- @field #list ROE
SUPPRESSION.ROE={
Hold="Weapon Hold",
Free="Weapon Free",
Return="Return Fire",
}
--- Enumerator of possible alarm states.
-- @field #list AlarmState
SUPPRESSION.AlarmState={
Auto="Auto",
Green="Green",
Red="Red",
}
--- Main F10 menu for suppresion, i.e. F10/Suppression.
-- @field #string MenuF10
SUPPRESSION.MenuF10=nil
--- PSEUDOATC version.
-- @field #number version
SUPPRESSION.version="0.9.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--TODO list
--DONE: Figure out who was shooting and move away from him.
--DONE: Move behind a scenery building if there is one nearby.
--DONE: Retreat to a given zone or point.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Creates a new AI_suppression object.
-- @param #SUPPRESSION self
-- @param Wrapper.Group#GROUP group The GROUP object for which suppression should be applied.
-- @return #SUPPRESSION SUPPRESSION object.
-- @return nil If group does not exist or is not a ground group.
function SUPPRESSION:New(group)
-- Inherits from FSM_CONTROLLABLE
local self=BASE:Inherit(self, FSM_CONTROLLABLE:New()) -- #SUPPRESSION
-- Check that group is present.
if group then
self.lid=string.format("SUPPRESSION %s | ", tostring(group:GetName()))
self:T(self.lid..string.format("SUPPRESSION version %s. Activating suppressive fire for group %s", SUPPRESSION.version, group:GetName()))
else
self:E(self.lid.."SUPPRESSION | 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
self:E(self.lid..string.format("SUPPRESSION fire group %s has to be a GROUND group!", group:GetName()))
return nil
end
-- Set the controllable for the FSM.
self:SetControllable(group)
-- Get DCS descriptors of group.
local DCSgroup=Group.getByName(group:GetName())
local DCSunit=DCSgroup:getUnit(1)
self.DCSdesc=DCSunit:getDesc()
-- Get max speed the group can do and convert to km/h.
self.SpeedMax=self.DCSdesc.speedMaxOffRoad*3.6
-- Set speed to maximum.
self.Speed=self.SpeedMax
-- Is this infantry or not.
self.IsInfantry=DCSunit:hasAttribute("Infantry")
-- Type of group.
self.Type=group:GetTypeName()
-- Initial group strength.
self.IniGroupStrength=#group:GetUnits()
-- Set ROE and Alarm State.
self:SetDefaultROE("Free")
self:SetDefaultAlarmState("Auto")
-- Transitions
self:AddTransition("*", "Start", "CombatReady")
self:AddTransition("*", "Status", "*")
self:AddTransition("CombatReady", "Hit", "Suppressed")
self:AddTransition("Suppressed", "Hit", "Suppressed")
self:AddTransition("Suppressed", "Recovered", "CombatReady")
self:AddTransition("Suppressed", "TakeCover", "TakingCover")
self:AddTransition("Suppressed", "FallBack", "FallingBack")
self:AddTransition("*", "Retreat", "Retreating")
self:AddTransition("TakingCover", "FightBack", "CombatReady")
self:AddTransition("FallingBack", "FightBack", "CombatReady")
self:AddTransition("Retreating", "Retreated", "Retreated")
self:AddTransition("*", "Dead", "*")
self:AddTransition("TakingCover", "Hit", "TakingCover")
self:AddTransition("FallingBack", "Hit", "FallingBack")
--- Trigger "Status" event.
-- @function [parent=#SUPPRESSION] Status
-- @param #SUPPRESSION self
--- Trigger "Status" event after a delay.
-- @function [parent=#SUPPRESSION] __Status
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
--- User function for OnAfter "Status" event.
-- @function [parent=#SUPPRESSION] OnAfterStatus
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Trigger "Hit" event.
-- @function [parent=#SUPPRESSION] Hit
-- @param #SUPPRESSION self
-- @param Wrapper.Unit#UNIT Unit Unit that was hit.
-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked.
--- Trigger "Hit" event after a delay.
-- @function [parent=#SUPPRESSION] __Hit
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
-- @param Wrapper.Unit#UNIT Unit Unit that was hit.
-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked.
--- User function for OnBefore "Hit" event.
-- @function [parent=#SUPPRESSION] OnBeforeHit
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT Unit Unit that was hit.
-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked.
-- @return #boolean
--- User function for OnAfter "Hit" event.
-- @function [parent=#SUPPRESSION] OnAfterHit
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT Unit Unit that was hit.
-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked.
--- Trigger "Recovered" event.
-- @function [parent=#SUPPRESSION] Recovered
-- @param #SUPPRESSION self
--- Trigger "Recovered" event after a delay.
-- @function [parent=#SUPPRESSION] Recovered
-- @param #number Delay Delay in seconds.
-- @param #SUPPRESSION self
--- User function for OnBefore "Recovered" event.
-- @function [parent=#SUPPRESSION] OnBeforeRecovered
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean
--- User function for OnAfter "Recovered" event.
-- @function [parent=#SUPPRESSION] OnAfterRecovered
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Trigger "TakeCover" event.
-- @function [parent=#SUPPRESSION] TakeCover
-- @param #SUPPRESSION self
-- @param Core.Point#COORDINATE Hideout Place where the group will hide.
--- Trigger "TakeCover" event after a delay.
-- @function [parent=#SUPPRESSION] __TakeCover
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
-- @param Core.Point#COORDINATE Hideout Place where the group will hide.
--- User function for OnBefore "TakeCover" event.
-- @function [parent=#SUPPRESSION] OnBeforeTakeCover
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Hideout Place where the group will hide.
-- @return #boolean
--- User function for OnAfter "TakeCover" event.
-- @function [parent=#SUPPRESSION] OnAfterTakeCover
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Hideout Place where the group will hide.
--- Trigger "FallBack" event.
-- @function [parent=#SUPPRESSION] FallBack
-- @param #SUPPRESSION self
-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this.
--- Trigger "FallBack" event after a delay.
-- @function [parent=#SUPPRESSION] __FallBack
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this.
--- User function for OnBefore "FallBack" event.
-- @function [parent=#SUPPRESSION] OnBeforeFallBack
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this.
-- @return #boolean
--- User function for OnAfter "FallBack" event.
-- @function [parent=#SUPPRESSION] OnAfterFallBack
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this.
--- Trigger "Retreat" event.
-- @function [parent=#SUPPRESSION] Retreat
-- @param #SUPPRESSION self
--- Trigger "Retreat" event after a delay.
-- @function [parent=#SUPPRESSION] __Retreat
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
--- User function for OnBefore "Retreat" event.
-- @function [parent=#SUPPRESSION] OnBeforeRetreat
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean
--- User function for OnAfter "Retreat" event.
-- @function [parent=#SUPPRESSION] OnAfterRetreat
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Trigger "Retreated" event.
-- @function [parent=#SUPPRESSION] Retreated
-- @param #SUPPRESSION self
--- Trigger "Retreated" event after a delay.
-- @function [parent=#SUPPRESSION] __Retreated
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
--- User function for OnBefore "Retreated" event.
-- @function [parent=#SUPPRESSION] OnBeforeRetreated
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean
--- User function for OnAfter "Retreated" event.
-- @function [parent=#SUPPRESSION] OnAfterRetreated
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Trigger "FightBack" event.
-- @function [parent=#SUPPRESSION] FightBack
-- @param #SUPPRESSION self
--- Trigger "FightBack" event after a delay.
-- @function [parent=#SUPPRESSION] __FightBack
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
--- User function for OnBefore "FlightBack" event.
-- @function [parent=#SUPPRESSION] OnBeforeFightBack
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean
--- User function for OnAfter "FlightBack" event.
-- @function [parent=#SUPPRESSION] OnAfterFightBack
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Trigger "Dead" event.
-- @function [parent=#SUPPRESSION] Dead
-- @param #SUPPRESSION self
--- Trigger "Dead" event after a delay.
-- @function [parent=#SUPPRESSION] __Dead
-- @param #SUPPRESSION self
-- @param #number Delay Delay in seconds.
--- User function for OnAfter "Dead" event.
-- @function [parent=#SUPPRESSION] OnAfterDead
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set average, minimum and maximum time a unit is suppressed each time it gets hit.
-- @param #SUPPRESSION self
-- @param #number Tave Average time [seconds] a group will be suppressed. Default is 15 seconds.
-- @param #number Tmin (Optional) Minimum time [seconds] a group will be suppressed. Default is 5 seconds.
-- @param #number Tmax (Optional) Maximum time a group will be suppressed. Default is 25 seconds.
function SUPPRESSION:SetSuppressionTime(Tave, Tmin, Tmax)
self:F({Tave=Tave, Tmin=Tmin, Tmax=Tmax})
-- Minimum suppression time is input or default but at least 1 second.
self.Tsuppress_min=Tmin or self.Tsuppress_min
self.Tsuppress_min=math.max(self.Tsuppress_min, 1)
-- Maximum suppression time is input or dault but at least Tmin.
self.Tsuppress_max=Tmax or self.Tsuppress_max
self.Tsuppress_max=math.max(self.Tsuppress_max, self.Tsuppress_min)
-- Expected suppression time is input or default but at leat Tmin and at most Tmax.
self.Tsuppress_ave=Tave or self.Tsuppress_ave
self.Tsuppress_ave=math.max(self.Tsuppress_min)
self.Tsuppress_ave=math.min(self.Tsuppress_max)
self:T(self.lid..string.format("Set ave suppression time to %d seconds.", self.Tsuppress_ave))
self:T(self.lid..string.format("Set min suppression time to %d seconds.", self.Tsuppress_min))
self:T(self.lid..string.format("Set max suppression time to %d seconds.", self.Tsuppress_max))
end
--- Set the zone to which a group retreats after being damaged too much.
-- @param #SUPPRESSION self
-- @param Core.Zone#ZONE zone MOOSE zone object.
function SUPPRESSION:SetRetreatZone(zone)
self:F({zone=zone})
self.RetreatZone=zone
end
--- Turn Debug mode on. Enables messages and more output to DCS log file.
-- @param #SUPPRESSION self
function SUPPRESSION:DebugOn()
self:F()
self.Debug=true
end
--- Flare units when they are hit, die or recover from suppression.
-- @param #SUPPRESSION self
function SUPPRESSION:FlareOn()
self:F()
self.flare=true
end
--- Smoke positions where units fall back to, hide or retreat.
-- @param #SUPPRESSION self
function SUPPRESSION:SmokeOn()
self:F()
self.smoke=true
end
--- Set the formation a group uses for fall back, hide or retreat.
-- @param #SUPPRESSION self
-- @param #string formation Formation of the group. Default "Vee".
function SUPPRESSION:SetFormation(formation)
self:F(formation)
self.Formation=formation or "Vee"
end
--- Set speed a group moves at for fall back, hide or retreat.
-- @param #SUPPRESSION self
-- @param #number speed Speed in km/h of group. Default max speed the group can do.
function SUPPRESSION:SetSpeed(speed)
self:F(speed)
self.Speed=speed or self.SpeedMax
self.Speed=math.min(self.Speed, self.SpeedMax)
end
--- Enable fall back if a group is hit.
-- @param #SUPPRESSION self
-- @param #boolean switch Enable=true or disable=false fall back of group.
function SUPPRESSION:Fallback(switch)
self:F(switch)
if switch==nil then
switch=true
end
self.FallbackON=switch
end
--- Set distance a group will fall back when it gets hit.
-- @param #SUPPRESSION self
-- @param #number distance Distance in meters.
function SUPPRESSION:SetFallbackDistance(distance)
self:F(distance)
self.FallbackDist=distance
end
--- Set time a group waits at its fall back position before it resumes its normal mission.
-- @param #SUPPRESSION self
-- @param #number time Time in seconds.
function SUPPRESSION:SetFallbackWait(time)
self:F(time)
self.FallbackWait=time
end
--- Enable take cover option if a unit is hit.
-- @param #SUPPRESSION self
-- @param #boolean switch Enable=true or disable=false fall back of group.
function SUPPRESSION:Takecover(switch)
self:F(switch)
if switch==nil then
switch=true
end
self.TakecoverON=switch
end
--- Set time a group waits at its hideout position before it resumes its normal mission.
-- @param #SUPPRESSION self
-- @param #number time Time in seconds.
function SUPPRESSION:SetTakecoverWait(time)
self:F(time)
self.TakecoverWait=time
end
--- Set distance a group searches for hideout places.
-- @param #SUPPRESSION self
-- @param #number range Search range in meters.
function SUPPRESSION:SetTakecoverRange(range)
self:F(range)
self.TakecoverRange=range
end
--- Set hideout place explicitly.
-- @param #SUPPRESSION self
-- @param Core.Point#COORDINATE Hideout Place where the group will hide after the TakeCover event.
function SUPPRESSION:SetTakecoverPlace(Hideout)
self.hideout=Hideout
end
--- Set minimum probability that a group flees (falls back or takes cover) after a hit event. Default is 10%.
-- @param #SUPPRESSION self
-- @param #number probability Probability in percent.
function SUPPRESSION:SetMinimumFleeProbability(probability)
self:F(probability)
self.PminFlee=probability or 10
end
--- Set maximum probability that a group flees (falls back or takes cover) after a hit event. Default is 90%.
-- @param #SUPPRESSION self
-- @param #number probability Probability in percent.
function SUPPRESSION:SetMaximumFleeProbability(probability)
self:F(probability)
self.PmaxFlee=probability or 90
end
--- Set damage threshold before a group is ordered to retreat if a retreat zone was defined.
-- If the group consists of only a singe unit, this referrs to the life of the unit.
-- If the group consists of more than one unit, this referrs to the group strength relative to its initial strength.
-- @param #SUPPRESSION self
-- @param #number damage Damage in percent. If group gets damaged above this value, the group will retreat. Default 50 %.
function SUPPRESSION:SetRetreatDamage(damage)
self:F(damage)
self.RetreatDamage=damage or 50
end
--- Set time a group waits in the retreat zone before it resumes its mission. Default is two hours.
-- @param #SUPPRESSION self
-- @param #number time Time in seconds. Default 7200 seconds = 2 hours.
function SUPPRESSION:SetRetreatWait(time)
self:F(time)
self.RetreatWait=time or 7200
end
--- Set alarm state a group will get after it returns from a fall back or take cover.
-- @param #SUPPRESSION self
-- @param #string alarmstate Alarm state. Possible "Auto", "Green", "Red". Default is "Auto".
function SUPPRESSION:SetDefaultAlarmState(alarmstate)
self:F(alarmstate)
if alarmstate:lower()=="auto" then
self.DefaultAlarmState=SUPPRESSION.AlarmState.Auto
elseif alarmstate:lower()=="green" then
self.DefaultAlarmState=SUPPRESSION.AlarmState.Green
elseif alarmstate:lower()=="red" then
self.DefaultAlarmState=SUPPRESSION.AlarmState.Red
else
self.DefaultAlarmState=SUPPRESSION.AlarmState.Auto
end
end
--- Set Rules of Engagement (ROE) a group will get when it recovers from suppression.
-- @param #SUPPRESSION self
-- @param #string roe ROE after suppression. Possible "Free", "Hold" or "Return". Default "Free".
function SUPPRESSION:SetDefaultROE(roe)
self:F(roe)
if roe:lower()=="free" then
self.DefaultROE=SUPPRESSION.ROE.Free
elseif roe:lower()=="hold" then
self.DefaultROE=SUPPRESSION.ROE.Hold
elseif roe:lower()=="return" then
self.DefaultROE=SUPPRESSION.ROE.Return
else
self.DefaultROE=SUPPRESSION.ROE.Free
end
end
--- Create an F10 menu entry for the suppressed group. The menu is mainly for Debugging purposes.
-- @param #SUPPRESSION self
-- @param #boolean switch Enable=true or disable=false menu group. Default is true.
function SUPPRESSION:MenuOn(switch)
self:F(switch)
if switch==nil then
switch=true
end
self.MenuON=switch
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create F10 main menu, i.e. F10/Suppression. The menu is mainly for Debugging purposes.
-- @param #SUPPRESSION self
function SUPPRESSION:_CreateMenuGroup()
local SubMenuName=self.Controllable:GetName()
local MenuGroup=MENU_MISSION:New(SubMenuName, SUPPRESSION.MenuF10)
MENU_MISSION_COMMAND:New("Fallback!", MenuGroup, self.OrderFallBack, self)
MENU_MISSION_COMMAND:New("Take Cover!", MenuGroup, self.OrderTakeCover, self)
MENU_MISSION_COMMAND:New("Retreat!", MenuGroup, self.OrderRetreat, self)
MENU_MISSION_COMMAND:New("Report Status", MenuGroup, self.Status, self, true)
end
--- Order group to fall back between 100 and 150 meters in a random direction.
-- @param #SUPPRESSION self
function SUPPRESSION:OrderFallBack()
local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE
local vicinity=group:GetCoordinate():GetRandomVec2InRadius(150, 100)
local coord=COORDINATE:NewFromVec2(vicinity)
self:FallBack(self.Controllable)
end
--- Order group to take cover at a nearby scenery object.
-- @param #SUPPRESSION self
function SUPPRESSION:OrderTakeCover()
-- Search place to hide or take specified one.
local Hideout=self.hideout
if self.hideout==nil then
Hideout=self:_SearchHideout()
end
-- Trigger TakeCover event.
self:TakeCover(Hideout)
end
--- Order group to retreat to a pre-defined zone.
-- @param #SUPPRESSION self
function SUPPRESSION:OrderRetreat()
self:Retreat()
end
--- Status of group. Current ROE, alarm state, life.
-- @param #SUPPRESSION self
-- @param #boolean message Send message to all players.
function SUPPRESSION:StatusReport(message)
local group=self.Controllable --Wrapper.Group#GROUP
local nunits=group:CountAliveUnits()
local roe=self.CurrentROE
local state=self.CurrentAlarmState
local life_min, life_max, life_ave, life_ave0, groupstrength=self:_GetLife()
local ammotot=self.Controllable:GetAmmunition()
--[[
local text=string.format("Status of group %s\n", name)
text=text..string.format("Number of units: %d of %d\n", nunits, self.IniGroupStrength)
text=text..string.format("Current state: %s\n", self:GetState())
text=text..string.format("ROE: %s\n", roe)
text=text..string.format("Alarm state: %s\n", state)
text=text..string.format("Hits taken: %d\n", self.Nhit)
text=text..string.format("Life min: %3.0f\n", life_min)
text=text..string.format("Life max: %3.0f\n", life_max)
text=text..string.format("Life ave: %3.0f\n", life_ave)
text=text..string.format("Life ave0: %3.0f\n", life_ave0)
text=text..string.format("Ammo tot: %d\n", at)
text=text..string.format("Group strength: %3.0f", groupstrength)
]]
local text=string.format("State %s, Units=%d/%d, ROE=%s Alarm State=%s, Hits=%d, Life=%d/%d/%d/%d, Ammo=%d",
self:GetState(), nunits, self.IniGroupStrength, self.CurrentROE, self.CurrentAlarmState, self.Nhit, life_min, life_max, life_ave, life_ave0, ammotot)
MESSAGE:New(text, 10):ToAllIf(message or self.Debug)
self:I(self.lid.."\n"..text)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- After "Start" event. Initialized ROE and alarm state. Starts the event handler.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onafterStart(Controllable, From, Event, To)
self:_EventFromTo("onafterStart", Event, From, To)
local text=string.format("Started SUPPRESSION for group %s.", Controllable:GetName())
self:I(self.lid..text)
MESSAGE:New(text, 10):ToAllIf(self.Debug)
local rzone="not defined"
if self.RetreatZone then
rzone=self.RetreatZone:GetName()
end
-- Set retreat damage value if it was not set by user input.
if self.RetreatDamage==nil then
if self.RetreatZone then
if self.IniGroupStrength==1 then
self.RetreatDamage=60.0 -- 40% of life is left.
elseif self.IniGroupStrength==2 then
self.RetreatDamage=50.0 -- 50% of group left, i.e. 1 of 2. We already order a retreat, because if for a group 2 two a zone is defined it would not be used at all.
else
self.RetreatDamage=66.5 -- 34% of the group is left, e.g. 1 of 3,4 or 5, 2 of 6,7 or 8, 3 of 9,10 or 11, 4/12, 4/13, 4/14, 5/15, ...
end
else
self.RetreatDamage=100 -- If no retreat then this should be set to 100%.
end
end
-- Create main F10 menu if it is not there yet.
if self.MenuON then
if not SUPPRESSION.MenuF10 then
SUPPRESSION.MenuF10 = MENU_MISSION:New("Suppression")
end
self:_CreateMenuGroup()
end
-- Set the current ROE and alam state.
self:_SetAlarmState(self.DefaultAlarmState)
self:_SetROE(self.DefaultROE)
local text=string.format("\n******************************************************\n")
text=text..string.format("Suppressed group = %s\n", Controllable:GetName())
text=text..string.format("Type = %s\n", self.Type)
text=text..string.format("IsInfantry = %s\n", tostring(self.IsInfantry))
text=text..string.format("Group strength = %d\n", self.IniGroupStrength)
text=text..string.format("Average time = %5.1f seconds\n", self.Tsuppress_ave)
text=text..string.format("Minimum time = %5.1f seconds\n", self.Tsuppress_min)
text=text..string.format("Maximum time = %5.1f seconds\n", self.Tsuppress_max)
text=text..string.format("Default ROE = %s\n", self.DefaultROE)
text=text..string.format("Default AlarmState = %s\n", self.DefaultAlarmState)
text=text..string.format("Fall back ON = %s\n", tostring(self.FallbackON))
text=text..string.format("Fall back distance = %5.1f m\n", self.FallbackDist)
text=text..string.format("Fall back wait = %5.1f seconds\n", self.FallbackWait)
text=text..string.format("Fall back heading = %s degrees\n", tostring(self.FallbackHeading))
text=text..string.format("Take cover ON = %s\n", tostring(self.TakecoverON))
text=text..string.format("Take cover search = %5.1f m\n", self.TakecoverRange)
text=text..string.format("Take cover wait = %5.1f seconds\n", self.TakecoverWait)
text=text..string.format("Min flee probability = %5.1f\n", self.PminFlee)
text=text..string.format("Max flee probability = %5.1f\n", self.PmaxFlee)
text=text..string.format("Retreat zone = %s\n", rzone)
text=text..string.format("Retreat damage = %5.1f %%\n", self.RetreatDamage)
text=text..string.format("Retreat wait = %5.1f seconds\n", self.RetreatWait)
text=text..string.format("Speed = %5.1f km/h\n", self.Speed)
text=text..string.format("Speed max = %5.1f km/h\n", self.SpeedMax)
text=text..string.format("Formation = %s\n", self.Formation)
text=text..string.format("******************************************************\n")
self:T(self.lid..text)
-- Add event handler.
if self.eventmoose then
self:HandleEvent(EVENTS.Hit, self._OnEventHit)
self:HandleEvent(EVENTS.Dead, self._OnEventDead)
else
world.addEventHandler(self)
end
self:__Status(-1)
end
--- After "Status" event.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onafterStatus(Controllable, From, Event, To)
--local text=string.format("State=%s, ROE %d, Life=%.1f", Controllable:GetName())
--MESSAGE:New(text, 10):ToAllIf(self.Debug)
local group=self.Controllable --Wrapper.Group#GROUP
local n=group:GetAmmunition()
self:StatusReport(false)
-- Retreat if completely out of ammo and retreat zone defined.
if n==0 and self.RetreatZone then
self:Retreat()
end
self:__Status(-30)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Hit" event. (Of course, this is not really before the group got hit.)
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT Unit Unit that was hit.
-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked.
-- @return boolean
function SUPPRESSION:onbeforeHit(Controllable, From, Event, To, Unit, AttackUnit)
self:_EventFromTo("onbeforeHit", Event, From, To)
--local Tnow=timer.getTime()
--env.info(self.lid..string.format("Last hit = %s %s", tostring(self.LastHit), tostring(Tnow)))
return true
end
--- After "Hit" event.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT Unit Unit that was hit.
-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked.
function SUPPRESSION:onafterHit(Controllable, From, Event, To, Unit, AttackUnit)
self:_EventFromTo("onafterHit", Event, From, To)
-- Suppress unit.
if From=="CombatReady" or From=="Suppressed" then
self:_Suppress()
end
-- Get life of group in %.
local life_min, life_max, life_ave, life_ave0, groupstrength=self:_GetLife()
-- Damage in %. If group consists only of one unit, we take its life value.
local Damage=100-life_ave0
-- Condition for retreat.
local RetreatCondition = Damage >= self.RetreatDamage-0.01 and self.RetreatZone
-- Probability that a unit flees. The probability increases linearly with the damage of the group/unit.
-- If Damage=0 ==> P=Pmin
-- if Damage=RetreatDamage ==> P=Pmax
-- If no retreat zone has been specified, RetreatDamage is 100.
local Pflee=(self.PmaxFlee-self.PminFlee)/self.RetreatDamage * math.min(Damage, self.RetreatDamage) + self.PminFlee
-- Evaluate flee condition.
local P=math.random(0,100)
local FleeCondition = P < Pflee
local text
text=string.format("\nGroup %s: Life min=%5.1f, max=%5.1f, ave=%5.1f, ave0=%5.1f group=%5.1f\n", Controllable:GetName(), life_min, life_max, life_ave, life_ave0, groupstrength)
text=string.format("Group %s: Damage = %8.4f (%8.4f retreat threshold).\n", Controllable:GetName(), Damage, self.RetreatDamage)
text=string.format("Group %s: P_Flee = %5.1f %5.1f=P_rand (P_Flee > Prand ==> Flee)\n", Controllable:GetName(), Pflee, P)
self:T(self.lid..text)
-- Group is obviously destroyed.
if Damage >= 99.9 then
return
end
if RetreatCondition then
-- Trigger Retreat event.
self:Retreat()
elseif FleeCondition then
if self.FallbackON and AttackUnit:IsGround() then
-- Trigger FallBack event.
self:FallBack(AttackUnit)
elseif self.TakecoverON then
-- Search place to hide or take specified one.
local Hideout=self.hideout
if self.hideout==nil then
Hideout=self:_SearchHideout()
end
-- Trigger TakeCover event.
self:TakeCover(Hideout)
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Recovered" event. Check if suppression time is over.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean
function SUPPRESSION:onbeforeRecovered(Controllable, From, Event, To)
self:_EventFromTo("onbeforeRecovered", Event, From, To)
-- Current time.
local Tnow=timer.getTime()
-- Debug info
self:T(self.lid..string.format("onbeforeRecovered: Time now: %d - Time over: %d", Tnow, self.TsuppressionOver))
-- Recovery is only possible if enough time since the last hit has passed.
if Tnow >= self.TsuppressionOver then
return true
else
return false
end
end
--- After "Recovered" event. Group has recovered and its ROE is set back to the "normal" unsuppressed state. Optionally the group is flared green.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onafterRecovered(Controllable, From, Event, To)
self:_EventFromTo("onafterRecovered", Event, From, To)
if Controllable and Controllable:IsAlive() then
-- Debug message.
local text=string.format("Group %s has recovered!", Controllable:GetName())
MESSAGE:New(text, 10):ToAllIf(self.Debug)
self:T(self.lid..text)
-- Set ROE back to default.
self:_SetROE()
-- Flare unit green.
if self.flare or self.Debug then
Controllable:FlareGreen()
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- After "FightBack" event. ROE and Alarm state are set back to default.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onafterFightBack(Controllable, From, Event, To)
self:_EventFromTo("onafterFightBack", Event, From, To)
-- Set ROE and alarm state back to default.
self:_SetROE()
self:_SetAlarmState()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "FallBack" event. We check that group is not already falling back.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this.
-- @return #boolean
function SUPPRESSION:onbeforeFallBack(Controllable, From, Event, To, AttackUnit)
self:_EventFromTo("onbeforeFallBack", Event, From, To)
--TODO: Add retreat? Only allowd transition is Suppressed-->Fallback. So in principle no need.
if From == "FallingBack" then
return false
else
return true
end
end
--- After "FallBack" event. We get the heading away from the attacker and route the group a certain distance in that direction.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this.
function SUPPRESSION:onafterFallBack(Controllable, From, Event, To, AttackUnit)
self:_EventFromTo("onafterFallback", Event, From, To)
-- Debug info
self:T(self.lid..string.format("Group %s is falling back after %d hits.", Controllable:GetName(), self.Nhit))
-- Coordinate of the attacker and attacked unit.
local ACoord=AttackUnit:GetCoordinate()
local DCoord=Controllable:GetCoordinate()
-- Heading from attacker to attacked unit.
local heading=self:_Heading(ACoord, DCoord)
-- Overwrite heading with user specified heading.
if self.FallbackHeading then
heading=self.FallbackHeading
end
-- Create a coordinate ~ 100 m in opposite direction of the attacking unit.
local Coord=DCoord:Translate(self.FallbackDist, heading)
-- Place marker
if self.Debug then
local MarkerID=Coord:MarkToAll("Fall back position for group "..Controllable:GetName())
end
-- Smoke the coordinate.
if self.smoke or self.Debug then
Coord:SmokeBlue()
end
-- Set ROE to weapon hold.
self:_SetROE(SUPPRESSION.ROE.Hold)
-- Set alarm state to GREEN and let the unit run away.
self:_SetAlarmState(SUPPRESSION.AlarmState.Green)
-- Make the group run away.
self:_Run(Coord, self.Speed, self.Formation, self.FallbackWait)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "TakeCover" event. Search an area around the group for possible scenery objects where the group can hide.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Hideout Place where the group will hide.
-- @return #boolean
function SUPPRESSION:onbeforeTakeCover(Controllable, From, Event, To, Hideout)
self:_EventFromTo("onbeforeTakeCover", Event, From, To)
--TODO: Need to test this!
if From=="TakingCover" then
return false
end
-- Block transition if no hideout place is given.
if Hideout ~= nil then
return true
else
return false
end
end
--- After "TakeCover" event. Group will run to a nearby scenery object and "hide" there for a certain time.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Hideout Place where the group will hide.
function SUPPRESSION:onafterTakeCover(Controllable, From, Event, To, Hideout)
self:_EventFromTo("onafterTakeCover", Event, From, To)
if self.Debug then
local MarkerID=Hideout:MarkToAll(string.format("Hideout for group %s", Controllable:GetName()))
end
-- Smoke place of hideout.
if self.smoke or self.Debug then
Hideout:SmokeBlue()
end
-- Set ROE to weapon hold.
self:_SetROE(SUPPRESSION.ROE.Hold)
-- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire.
self:_SetAlarmState(SUPPRESSION.AlarmState.Green)
-- Make the group run away.
self:_Run(Hideout, self.Speed, self.Formation, self.TakecoverWait)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Retreat" event. We check that the group is not already retreating.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean True if transition is allowed, False if transition is forbidden.
function SUPPRESSION:onbeforeRetreat(Controllable, From, Event, To)
self:_EventFromTo("onbeforeRetreat", Event, From, To)
if From=="Retreating" then
local text=string.format("Group %s is already retreating.", tostring(Controllable:GetName()))
self:T2(self.lid..text)
return false
else
return true
end
end
--- After "Retreat" event. Find a random point in the retreat zone and route the group there.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onafterRetreat(Controllable, From, Event, To)
self:_EventFromTo("onafterRetreat", Event, From, To)
-- Route the group to a zone.
local text=string.format("Group %s is retreating! Alarm state green.", Controllable:GetName())
MESSAGE:New(text, 10):ToAllIf(self.Debug)
self:T(self.lid..text)
-- Get a random point in the retreat zone.
local ZoneCoord=self.RetreatZone:GetRandomCoordinate() -- Core.Point#COORDINATE
local ZoneVec2=ZoneCoord:GetVec2()
-- Debug smoke zone and point.
if self.smoke or self.Debug then
ZoneCoord:SmokeBlue()
end
if self.Debug then
self.RetreatZone:SmokeZone(SMOKECOLOR.Red, 12)
end
-- Set ROE to weapon hold.
self:_SetROE(SUPPRESSION.ROE.Hold)
-- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire.
self:_SetAlarmState(SUPPRESSION.AlarmState.Green)
-- Make unit run to retreat zone and wait there for ~two hours.
self:_Run(ZoneCoord, self.Speed, self.Formation, self.RetreatWait)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Before "Retreateded" event. Check that the group is really in the retreat zone.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onbeforeRetreated(Controllable, From, Event, To)
self:_EventFromTo("onbeforeRetreated", Event, From, To)
-- Check that the group is inside the zone.
local inzone=self.RetreatZone:IsVec3InZone(Controllable:GetVec3())
return inzone
end
--- After "Retreateded" event. Group has reached the retreat zone. Set ROE to return fire and alarm state to auto.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onafterRetreated(Controllable, From, Event, To)
self:_EventFromTo("onafterRetreated", Event, From, To)
-- Set ROE to weapon return fire.
self:_SetROE(SUPPRESSION.ROE.Return)
-- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire.
self:_SetAlarmState(SUPPRESSION.AlarmState.Auto)
-- TODO: Add hold task? Move from _Run()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- After "Dead" event, when a unit has died. When all units of a group are dead, FSM is stopped and eventhandler removed.
-- @param #SUPPRESSION self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SUPPRESSION:onafterDead(Controllable, From, Event, To)
self:_EventFromTo("onafterDead", Event, From, To)
-- Number of units left in the group.
local nunits=#self.Controllable:GetUnits()
local text=string.format("Group %s: One of our units just died! %d units left.", self.Controllable:GetName(), nunits)
MESSAGE:New(text, 10):ToAllIf(self.Debug)
self:T(self.lid..text)
-- Go to stop state.
if nunits==0 then
self:T(self.lid..string.format("Stopping SUPPRESSION for group %s.", Controllable:GetName()))
self:Stop()
if self.mooseevents then
self:UnHandleEvent(EVENTS.Dead)
self:UnHandleEvent(EVENTS.Hit)
else
world.removeEventHandler(self)
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event Handler
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event handler for suppressed groups.
--@param #SUPPRESSION self
function SUPPRESSION:onEvent(Event)
--self:E(event)
if Event == nil or Event.initiator == nil or Unit.getByName(Event.initiator:getName()) == nil then
return true
end
local EventData={}
if Event.initiator then
EventData.IniDCSUnit = Event.initiator
EventData.IniUnitName = Event.initiator:getName()
EventData.IniDCSGroup = Event.initiator:getGroup()
EventData.IniGroupName = Event.initiator:getGroup():getName()
EventData.IniGroup = GROUP:FindByName(EventData.IniGroupName)
EventData.IniUnit = UNIT:FindByName(EventData.IniUnitName)
end
if Event.target then
EventData.TgtDCSUnit = Event.target
EventData.TgtUnitName = Event.target:getName()
EventData.TgtDCSGroup = Event.target:getGroup()
EventData.TgtGroupName = Event.target:getGroup():getName()
EventData.TgtGroup = GROUP:FindByName(EventData.TgtGroupName)
EventData.TgtUnit = UNIT:FindByName(EventData.TgtUnitName)
end
-- Event HIT
if Event.id == world.event.S_EVENT_HIT then
self:_OnEventHit(EventData)
end
-- Event DEAD
if Event.id == world.event.S_EVENT_DEAD then
self:_OnEventDead(EventData)
end
end
--- Event handler for Dead event of suppressed groups.
-- @param #SUPPRESSION self
-- @param Core.Event#EVENTDATA EventData
function SUPPRESSION:_OnEventHit(EventData)
self:F(EventData)
local GroupNameSelf=self.Controllable:GetName()
local GroupNameTgt=EventData.TgtGroupName
local TgtUnit=EventData.TgtUnit
local tgt=EventData.TgtDCSUnit
local IniUnit=EventData.IniUnit
-- Check that correct group was hit.
if GroupNameTgt == GroupNameSelf then
self:T(self.lid..string.format("Hit event at t = %5.1f", timer.getTime()))
-- Flare unit that was hit.
if self.flare or self.Debug then
TgtUnit:FlareRed()
end
-- Increase Hit counter.
self.Nhit=self.Nhit+1
-- Info on hit times.
self:T(self.lid..string.format("Group %s has just been hit %d times.", self.Controllable:GetName(), self.Nhit))
--self:Status()
local life=tgt:getLife()/(tgt:getLife0()+1)*100
self:T2(self.lid..string.format("Target unit life = %5.1f", life))
-- FSM Hit event.
self:__Hit(3, TgtUnit, IniUnit)
end
end
--- Event handler for Dead event of suppressed groups.
-- @param #SUPPRESSION self
-- @param Core.Event#EVENTDATA EventData
function SUPPRESSION:_OnEventDead(EventData)
local GroupNameSelf=self.Controllable:GetName()
local GroupNameIni=EventData.IniGroupName
-- Check for correct group.
if GroupNameIni==GroupNameSelf then
-- Dead Unit.
local IniUnit=EventData.IniUnit --Wrapper.Unit#UNIT
local IniUnitName=EventData.IniUnitName
if EventData.IniUnit then
self:T2(self.lid..string.format("Group %s: Dead MOOSE unit DOES exist! Unit name %s.", GroupNameIni, IniUnitName))
else
self:T2(self.lid..string.format("Group %s: Dead MOOSE unit DOES NOT not exist! Unit name %s.", GroupNameIni, IniUnitName))
end
if EventData.IniDCSUnit then
self:T2(self.lid..string.format("Group %s: Dead DCS unit DOES exist! Unit name %s.", GroupNameIni, IniUnitName))
else
self:T2(self.lid..string.format("Group %s: Dead DCS unit DOES NOT exist! Unit name %s.", GroupNameIni, IniUnitName))
end
-- Flare unit that died.
if IniUnit and (self.flare or self.Debug) then
IniUnit:FlareWhite()
self:T(self.lid..string.format("Flare Dead MOOSE unit."))
end
-- Flare unit that died.
if EventData.IniDCSUnit and (self.flare or self.Debug) then
local p=EventData.IniDCSUnit:getPosition().p
trigger.action.signalFlare(p, trigger.flareColor.Yellow , 0)
self:T(self.lid..string.format("Flare Dead DCS unit."))
end
-- Get status.
self:Status()
-- FSM Dead event.
self:__Dead(0.1)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Suppress fire of a unit by setting its ROE to "Weapon Hold".
-- @param #SUPPRESSION self
function SUPPRESSION:_Suppress()
-- Current time.
local Tnow=timer.getTime()
-- Controllable
local Controllable=self.Controllable --Wrapper.Controllable#CONTROLLABLE
-- Group will hold their weapons.
self:_SetROE(SUPPRESSION.ROE.Hold)
-- Get randomized time the unit is suppressed.
local sigma=(self.Tsuppress_max-self.Tsuppress_min)/4
local Tsuppress=self:_Random_Gaussian(self.Tsuppress_ave,sigma,self.Tsuppress_min, self.Tsuppress_max)
-- 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.
if renew then
self:__Recovered(self.TsuppressionOver-Tnow)
end
-- Debug message.
local text=string.format("Group %s is suppressed for %d seconds. Suppression ends at %d:%02d.", Controllable:GetName(), Tsuppress, self.TsuppressionOver/60, self.TsuppressionOver%60)
MESSAGE:New(text, 10):ToAllIf(self.Debug)
self:T(self.lid..text)
end
--- Make group run/drive to a certain point. We put in several intermediate waypoints because sometimes the group stops before it arrived at the desired point.
--@param #SUPPRESSION self
--@param Core.Point#COORDINATE fin Coordinate where we want to go.
--@param #number speed Speed of group. Default is 20.
--@param #string formation Formation of group. Default is "Vee".
--@param #number wait Time the group will wait/hold at final waypoint. Default is 30 seconds.
function SUPPRESSION:_Run(fin, speed, formation, wait)
speed=speed or 20
formation=formation or "Off road"
wait=wait or 30
local group=self.Controllable -- Wrapper.Controllable#CONTROLLABLE
-- Clear all tasks.
group:ClearTasks()
-- Current coordinates of group.
local ini=group:GetCoordinate()
-- Distance between current and final point.
local dist=ini:Get2DDistance(fin)
-- Heading from ini to fin.
local heading=self:_Heading(ini, fin)
-- Number of waypoints.
local nx
if dist <= 50 then
nx=2
elseif dist <= 100 then
nx=3
elseif dist <= 500 then
nx=4
else
nx=5
end
-- Number of intermediate waypoints.
local dx=dist/(nx-1)
-- Waypoint and task arrays.
local wp={}
local tasks={}
-- First waypoint is the current position of the group.
wp[1]=ini:WaypointGround(speed, formation)
tasks[1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, 1, false)
if self.Debug then
local MarkerID=ini:MarkToAll(string.format("Waypoing %d of group %s (initial)", #wp, self.Controllable:GetName()))
end
self:T2(self.lid..string.format("Number of waypoints %d", nx))
for i=1,nx-2 do
local x=dx*i
local coord=ini:Translate(x, heading)
wp[#wp+1]=coord:WaypointGround(speed, formation)
tasks[#tasks+1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, #wp, false)
self:T2(self.lid..string.format("%d x = %4.1f", i, x))
if self.Debug then
local MarkerID=coord:MarkToAll(string.format("Waypoing %d of group %s", #wp, self.Controllable:GetName()))
end
end
self:T2(self.lid..string.format("Total distance: %4.1f", dist))
-- Final waypoint.
wp[#wp+1]=fin:WaypointGround(speed, formation)
if self.Debug then
local MarkerID=fin:MarkToAll(string.format("Waypoing %d of group %s (final)", #wp, self.Controllable:GetName()))
end
-- Task to hold.
local ConditionWait=group:TaskCondition(nil, nil, nil, nil, wait, nil)
local TaskHold = group:TaskHold()
-- Task combo to make group hold at final waypoint.
local TaskComboFin = {}
TaskComboFin[#TaskComboFin+1] = group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, #wp, true)
TaskComboFin[#TaskComboFin+1] = group:TaskControlled(TaskHold, ConditionWait)
-- Add final task.
tasks[#tasks+1]=group:TaskCombo(TaskComboFin)
-- Original waypoints of the group.
local Waypoints = group:GetTemplateRoutePoints()
-- New points are added to the default route.
for i,p in ipairs(wp) do
table.insert(Waypoints, i, wp[i])
end
-- Set task for all waypoints.
for i,wp in ipairs(Waypoints) do
group:SetTaskWaypoint(Waypoints[i], tasks[i])
end
-- Submit task and route group along waypoints.
group:Route(Waypoints)
end
--- Function called when group is passing a waypoint. At the last waypoint we set the group back to CombatReady.
--@param Wrapper.Group#GROUP group Group which is passing a waypoint.
--@param #SUPPRESSION Fsm The suppression object.
--@param #number i Waypoint number that has been reached.
--@param #boolean final True if it is the final waypoint. Start Fightback.
function SUPPRESSION._Passing_Waypoint(group, Fsm, i, final)
-- Debug message.
local text=string.format("Group %s passing waypoint %d (final=%s)", group:GetName(), i, tostring(final))
MESSAGE:New(text,10):ToAllIf(Fsm.Debug)
if Fsm.Debug then
env.info(self.lid..text)
end
if final then
if Fsm:is("Retreating") then
-- Retreated-->Retreated.
Fsm:Retreated()
else
-- FightBack-->Combatready: Change alarm state back to default.
Fsm:FightBack()
end
end
end
--- Search a place to hide. This is any scenery object in the vicinity.
--@param #SUPPRESSION self
--@return Core.Point#COORDINATE Coordinate of the hideout place.
--@return nil If no scenery object is within search radius.
function SUPPRESSION:_SearchHideout()
-- We search objects in a zone with radius ~300 m around the group.
local Zone = ZONE_GROUP:New("Zone_Hiding", self.Controllable, self.TakecoverRange)
local gpos = self.Controllable:GetCoordinate()
-- Scan for Scenery objects to run/drive to.
Zone:Scan(Object.Category.SCENERY)
-- Array with all possible hideouts, i.e. scenery objects in the vicinity of the group.
local hideouts={}
for SceneryTypeName, SceneryData in pairs(Zone:GetScannedScenery()) do
for SceneryName, SceneryObject in pairs(SceneryData) do
local SceneryObject = SceneryObject -- Wrapper.Scenery#SCENERY
-- Position of the scenery object.
local spos=SceneryObject:GetCoordinate()
-- Distance from group to hideout.
local distance= spos:Get2DDistance(gpos)
if self.Debug then
-- Place markers on every possible scenery object.
local MarkerID=SceneryObject:GetCoordinate():MarkToAll(string.format("%s scenery object %s", self.Controllable:GetName(),SceneryObject:GetTypeName()))
local text=string.format("%s scenery: %s, Coord %s", self.Controllable:GetName(), SceneryObject:GetTypeName(), SceneryObject:GetCoordinate():ToStringLLDMS())
self:T2(self.lid..text)
end
-- Add to table.
table.insert(hideouts, {object=SceneryObject, distance=distance})
end
end
-- Get random hideout place.
local Hideout=nil
if #hideouts>0 then
-- Debug info.
self:T(self.lid.."Number of hideouts "..#hideouts)
-- Sort results table wrt number of hits.
local _sort = function(a,b) return a.distance < b.distance end
table.sort(hideouts,_sort)
-- Pick a random location.
--Hideout=hideouts[math.random(#hideouts)].object
-- Pick closest location.
Hideout=hideouts[1].object:GetCoordinate()
else
self:E(self.lid.."No hideouts found!")
end
return Hideout
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 #SUPPRESSION self
-- @return #number Smallest life value of all units.
-- @return #number Largest life value of all units.
-- @return #number Average life value of all alife groups
-- @return #number Average life value of all groups including already dead ones.
-- @return #number Relative group strength.
function SUPPRESSION:_GetLife()
local group=self.Controllable --Wrapper.Group#GROUP
if group and group:IsAlive() then
local units=group:GetUnits()
local life_min=nil
local life_max=nil
local life_ave=0
local life_ave0=0
local n=0
local groupstrength=#units/self.IniGroupStrength*100
self.T2(self.lid..string.format("Group %s _GetLife nunits = %d", self.Controllable:GetName(), #units))
for _,unit in pairs(units) do
local unit=unit -- Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
n=n+1
local life=unit:GetLife()/(unit:GetLife0()+1)*100
if life_min==nil or life < life_min then
life_min=life
end
if life_max== nil or life > life_max then
life_max=life
end
life_ave=life_ave+life
if self.Debug then
local text=string.format("n=%02d: 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)
self:T2(self.lid..text)
end
end
end
-- If the counter did not increase (can happen!) return 0
if n==0 then
return 0,0,0,0,0
end
-- Average life relative to initial group strength including the dead ones.
life_ave0=life_ave/self.IniGroupStrength
-- Average life of all alive units.
life_ave=life_ave/n
return life_min, life_max, life_ave, life_ave0, groupstrength
else
return 0, 0, 0, 0, 0
end
end
--- Heading from point a to point b in degrees.
--@param #SUPPRESSION self
--@param Core.Point#COORDINATE a Coordinate.
--@param Core.Point#COORDINATE b Coordinate.
--@return #number angle Angle from a to b in degrees.
function SUPPRESSION:_Heading(a, b)
local dx = b.x-a.x
local dy = b.z-a.z
local angle = math.deg(math.atan2(dy,dx))
if angle < 0 then
angle = 360 + angle
end
return angle
end
--- Generate Gaussian pseudo-random numbers.
-- @param #SUPPRESSION self
-- @param #number x0 Expectation value of distribution.
-- @param #number sigma (Optional) Standard deviation. Default 10.
-- @param #number xmin (Optional) Lower cut-off value.
-- @param #number xmax (Optional) Upper cut-off value.
-- @return #number Gaussian random number.
function SUPPRESSION:_Random_Gaussian(x0, sigma, xmin, xmax)
-- Standard deviation. Default 5 if not given.
sigma=sigma or 5
local r
local gotit=false
local i=0
while not gotit do
-- Uniform numbers in [0,1). We need two.
local x1=math.random()
local x2=math.random()
-- Transform to Gaussian exp(-(x-x0)²/(2*sigma²).
r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0
i=i+1
if (r>=xmin and r<=xmax) or i>100 then
gotit=true
end
end
return r
end
--- Sets the ROE for the group and updates the current ROE variable.
-- @param #SUPPRESSION self
-- @param #string roe ROE the group will get. Possible "Free", "Hold", "Return". Default is self.DefaultROE.
function SUPPRESSION:_SetROE(roe)
local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE
-- If no argument is given, we take the default ROE.
roe=roe or self.DefaultROE
-- Update the current ROE.
self.CurrentROE=roe
-- Set the ROE.
if roe==SUPPRESSION.ROE.Free then
group:OptionROEOpenFire()
elseif roe==SUPPRESSION.ROE.Hold then
group:OptionROEHoldFire()
elseif roe==SUPPRESSION.ROE.Return then
group:OptionROEReturnFire()
else
self:E(self.lid.."Unknown ROE requested: "..tostring(roe))
group:OptionROEOpenFire()
self.CurrentROE=SUPPRESSION.ROE.Free
end
local text=string.format("Group %s now has ROE %s.", self.Controllable:GetName(), self.CurrentROE)
self:T(self.lid..text)
end
--- Sets the alarm state of the group and updates the current alarm state variable.
-- @param #SUPPRESSION self
-- @param #string state Alarm state the group will get. Possible "Auto", "Green", "Red". Default is self.DefaultAlarmState.
function SUPPRESSION:_SetAlarmState(state)
local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE
-- Input or back to default alarm state.
state=state or self.DefaultAlarmState
-- Update the current alam state of the group.
self.CurrentAlarmState=state
-- Set the alarm state.
if state==SUPPRESSION.AlarmState.Auto then
group:OptionAlarmStateAuto()
elseif state==SUPPRESSION.AlarmState.Green then
group:OptionAlarmStateGreen()
elseif state==SUPPRESSION.AlarmState.Red then
group:OptionAlarmStateRed()
else
self:E(self.lid.."Unknown alarm state requested: "..tostring(state))
group:OptionAlarmStateAuto()
self.CurrentAlarmState=SUPPRESSION.AlarmState.Auto
end
local text=string.format("Group %s now has Alarm State %s.", self.Controllable:GetName(), self.CurrentAlarmState)
self:T(self.lid..text)
end
--- Print event-from-to string to DCS log file.
-- @param #SUPPRESSION self
-- @param #string BA Before/after info.
-- @param #string Event Event.
-- @param #string From From state.
-- @param #string To To state.
function SUPPRESSION:_EventFromTo(BA, Event, From, To)
local text=string.format("\n%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To)
self:T2(self.lid..text)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------