From 0ad74610219a133bcfd4577427b30dc7dbf5ae17 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Thu, 6 May 2021 13:08:17 +0200 Subject: [PATCH] Delete MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked directory --- .../l10n/DEFAULT/MarkerOps_Base_Demo.lua | 86 - .../l10n/DEFAULT/Moose_dev_small.lua | 78101 ---------------- .../_unpacked/l10n/DEFAULT/dictionary | 9 - .../_unpacked/l10n/DEFAULT/mapResource | 5 - .../_unpacked/mission | 762 - .../_unpacked/options | 345 - .../_unpacked/theatre | 1 - .../_unpacked/warehouses | 845 - 8 files changed, 80154 deletions(-) delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/MarkerOps_Base_Demo.lua delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/Moose_dev_small.lua delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/dictionary delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/mapResource delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/mission delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/options delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/theatre delete mode 100644 MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/warehouses diff --git a/MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/MarkerOps_Base_Demo.lua b/MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/MarkerOps_Base_Demo.lua deleted file mode 100644 index 0ee97708dc..0000000000 --- a/MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/MarkerOps_Base_Demo.lua +++ /dev/null @@ -1,86 +0,0 @@ -------------------------------------------------------------------------- --- MOP-100 - MARKEROPS_BASE - Basic Demo -------------------------------------------------------------------------- --- Documentation --- --- MARKEROPS_BASE: https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Core.MarkerOps_Base.html --- -------------------------------------------------------------------------- --- On the F10, call a tanker to start from the carrier. It will fly to --- an initial zone. Set a marker on the F10 map with keyword "TankerDemo". --- The Tanker will fly there. Set a marker on the F10 map with keywords --- "TankerDemo RTB". The tanke will RTB to the carrier. -------------------------------------------------------------------------- --- Date: May 2021 -------------------------------------------------------------------------- - --- globals -mytanker = nil -tankergroup = nil -TankerAuftrag = nil - -function menucalltanker() - - if not mytanker then - -- new MARKEROPS_BASE object - mytanker = MARKEROPS_BASE:New("TankerDemo",{"RTB"}) -- Core.MarkerOps_Base#MARKEROPS_BASE - -- start FlightGroup - tankergroup = FLIGHTGROUP:New("Tanker") - tankergroup:SetHomebase(AIRBASE:FindByName("Truman")) - tankergroup:SetDefaultRadio(245,"AM",false) - tankergroup:SetDespawnAfterLanding() - tankergroup:SwitchTACAN(45, "TKR", 1, "X") - tankergroup:SetDefaultCallsign(CALLSIGN.Tanker.Texaco,1) - -- Mission - local InitialHold = ZONE:New("Initial Hold"):GetCoordinate() - TankerAuftrag = AUFTRAG:NewTANKER(InitialHold,18000,UTILS.KnotsToAltKIAS(220,18000),90,20,0) - TankerAuftrag:SetMissionRange(500) - tankergroup:AddMission(TankerAuftrag) - else - local status = tankergroup:GetState() - local m = MESSAGE:New(string.format("Tanker %s ops in status: %s", mytanker.Tag, status),10,"Info",true):ToAll() - end - - -- Handler function - local function Handler(Keywords,Coord) - - local MustRTB = false - for _,_word in pairs (Keywords) do - if string.lower(_word) == "rtb" then - MustRTB = true - end - end - - -- cancel current Auftrag - TankerAuftrag:Cancel() - - -- check if we need to RTB - if MustRTB then - tankergroup:RTB(AIRBASE:FindByName("Truman")) - else - -- no, fly to coordinate of marker - local auftrag = AUFTRAG:NewTANKER(Coord,18000,UTILS.KnotsToAltKIAS(220,18000),90,20,0) - auftrag:SetMissionRange(500) - tankergroup:AddMission(auftrag) - TankerAuftrag = auftrag - end - end - - -- Event functions - function mytanker:OnAfterMarkAdded(From,Event,To,Text,Keywords,Coord) - local m = MESSAGE:New(string.format("Tanker %s Mark Added.", self.Tag),10,"Info",true):ToAll() - Handler(Keywords,Coord) - end - - function mytanker:OnAfterMarkChanged(From,Event,To,Text,Keywords,Coord) - local m = MESSAGE:New(string.format("Tanker %s Mark Changed.", self.Tag),10,"Info",true):ToAll() - Handler(Keywords,Coord) - end - - function mytanker:OnAfterMarkDeleted(From,Event,To) - local m = MESSAGE:New(string.format("Tanker %s Mark Deleted.", self.Tag),10,"Info",true):ToAll() - end -end - -MenuTop = MENU_COALITION:New( coalition.side.BLUE,"Call Tanker") -MenuTanker = MENU_COALITION_COMMAND:New(coalition.side.BLUE,"Start Tanker",MenuTop,menucalltanker) diff --git a/MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/Moose_dev_small.lua b/MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/Moose_dev_small.lua deleted file mode 100644 index 71ade2a43b..0000000000 --- a/MOP - MarkerOps_Base/MOP-100 - MARKEROPS_BASE/_unpacked/l10n/DEFAULT/Moose_dev_small.lua +++ /dev/null @@ -1,78101 +0,0 @@ -env.info('*** MOOSE GITHUB Commit Hash ID: 2021-05-06T07:30:06.0000000Z-9eba6d607c4f2c10c460c7bf9c71d88affa5ff6c ***') -env.info('*** MOOSE STATIC INCLUDE START *** ') -ENUMS={} -ENUMS.ROE={ -WeaponFree=0, -OpenFireWeaponFree=1, -OpenFire=2, -ReturnFire=3, -WeaponHold=4, -} -ENUMS.ROT={ -NoReaction=0, -PassiveDefense=1, -EvadeFire=2, -BypassAndEscape=3, -AllowAbortMission=4, -} -ENUMS.AlarmState={ -Auto=0, -Green=1, -Red=2, -} -ENUMS.WeaponFlag={ -LGB=2, -TvGB=4, -SNSGB=8, -HEBomb=16, -Penetrator=32, -NapalmBomb=64, -FAEBomb=128, -ClusterBomb=256, -Dispencer=512, -CandleBomb=1024, -ParachuteBomb=2147483648, -LightRocket=2048, -MarkerRocket=4096, -CandleRocket=8192, -HeavyRocket=16384, -AntiRadarMissile=32768, -AntiShipMissile=65536, -AntiTankMissile=131072, -FireAndForgetASM=262144, -LaserASM=524288, -TeleASM=1048576, -CruiseMissile=2097152, -AntiRadarMissile2=1073741824, -SRAM=4194304, -MRAAM=8388608, -LRAAM=16777216, -IR_AAM=33554432, -SAR_AAM=67108864, -AR_AAM=134217728, -GunPod=268435456, -BuiltInCannon=536870912, -GuidedBomb=14, -AnyUnguidedBomb=2147485680, -AnyBomb=2147485694, -AnyRocket=30720, -GuidedASM=1572864, -TacticalASM=1835008, -AnyASM=4161536, -AnyASM2=1077903360, -AnyAAM=264241152, -AnyAutonomousMissile=36012032, -AnyMissile=268402688, -Cannons=805306368, -Auto=3221225470, -AutoDCS=1073741822, -AnyAG=2956984318, -AnyAA=264241152, -AnyUnguided=2952822768, -AnyGuided=268402702, -} -ENUMS.MissionTask={ -NOTHING="Nothing", -AFAC="AFAC", -ANTISHIPSTRIKE="Antiship Strike", -AWACS="AWACS", -CAP="CAP", -CAS="CAS", -ESCORT="Escort", -FIGHTERSWEEP="Fighter Sweep", -GROUNDATTACK="Ground Attack", -INTERCEPT="Intercept", -PINPOINTSTRIKE="Pinpoint Strike", -RECONNAISSANCE="Reconnaissance", -REFUELING="Refueling", -RUNWAYATTACK="Runway Attack", -SEAD="SEAD", -TRANSPORT="Transport", -} -ENUMS.Formation={} -ENUMS.Formation.FixedWing={} -ENUMS.Formation.FixedWing.LineAbreast={} -ENUMS.Formation.FixedWing.LineAbreast.Close=65537 -ENUMS.Formation.FixedWing.LineAbreast.Open=65538 -ENUMS.Formation.FixedWing.LineAbreast.Group=65539 -ENUMS.Formation.FixedWing.Trail={} -ENUMS.Formation.FixedWing.Trail.Close=131073 -ENUMS.Formation.FixedWing.Trail.Open=131074 -ENUMS.Formation.FixedWing.Trail.Group=131075 -ENUMS.Formation.FixedWing.Wedge={} -ENUMS.Formation.FixedWing.Wedge.Close=196609 -ENUMS.Formation.FixedWing.Wedge.Open=196610 -ENUMS.Formation.FixedWing.Wedge.Group=196611 -ENUMS.Formation.FixedWing.EchelonRight={} -ENUMS.Formation.FixedWing.EchelonRight.Close=262145 -ENUMS.Formation.FixedWing.EchelonRight.Open=262146 -ENUMS.Formation.FixedWing.EchelonRight.Group=262147 -ENUMS.Formation.FixedWing.EchelonLeft={} -ENUMS.Formation.FixedWing.EchelonLeft.Close=327681 -ENUMS.Formation.FixedWing.EchelonLeft.Open=327682 -ENUMS.Formation.FixedWing.EchelonLeft.Group=327683 -ENUMS.Formation.FixedWing.FingerFour={} -ENUMS.Formation.FixedWing.FingerFour.Close=393217 -ENUMS.Formation.FixedWing.FingerFour.Open=393218 -ENUMS.Formation.FixedWing.FingerFour.Group=393219 -ENUMS.Formation.FixedWing.Spread={} -ENUMS.Formation.FixedWing.Spread.Close=458753 -ENUMS.Formation.FixedWing.Spread.Open=458754 -ENUMS.Formation.FixedWing.Spread.Group=458755 -ENUMS.Formation.FixedWing.BomberElement={} -ENUMS.Formation.FixedWing.BomberElement.Close=786433 -ENUMS.Formation.FixedWing.BomberElement.Open=786434 -ENUMS.Formation.FixedWing.BomberElement.Group=786435 -ENUMS.Formation.FixedWing.BomberElementHeight={} -ENUMS.Formation.FixedWing.BomberElementHeight.Close=851968 -ENUMS.Formation.FixedWing.FighterVic={} -ENUMS.Formation.FixedWing.FighterVic.Close=917505 -ENUMS.Formation.FixedWing.FighterVic.Open=917506 -ENUMS.Formation.RotaryWing={} -ENUMS.Formation.RotaryWing.Column={} -ENUMS.Formation.RotaryWing.Column.D70=720896 -ENUMS.Formation.RotaryWing.Wedge={} -ENUMS.Formation.RotaryWing.Wedge.D70=8 -ENUMS.Formation.RotaryWing.FrontRight={} -ENUMS.Formation.RotaryWing.FrontRight.D300=655361 -ENUMS.Formation.RotaryWing.FrontRight.D600=655362 -ENUMS.Formation.RotaryWing.FrontLeft={} -ENUMS.Formation.RotaryWing.FrontLeft.D300=655617 -ENUMS.Formation.RotaryWing.FrontLeft.D600=655618 -ENUMS.Formation.RotaryWing.EchelonRight={} -ENUMS.Formation.RotaryWing.EchelonRight.D70=589825 -ENUMS.Formation.RotaryWing.EchelonRight.D300=589826 -ENUMS.Formation.RotaryWing.EchelonRight.D600=589827 -ENUMS.Formation.RotaryWing.EchelonLeft={} -ENUMS.Formation.RotaryWing.EchelonLeft.D70=590081 -ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082 -ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083 -ENUMS.Formation.Vehicle={} -ENUMS.Formation.Vehicle.Vee="Vee" -ENUMS.Formation.Vehicle.EchelonRight="EchelonR" -ENUMS.Formation.Vehicle.OffRoad="Off Road" -ENUMS.Formation.Vehicle.Rank="Rank" -ENUMS.Formation.Vehicle.EchelonLeft="EchelonL" -ENUMS.Formation.Vehicle.OnRoad="On Road" -ENUMS.Formation.Vehicle.Cone="Cone" -ENUMS.Formation.Vehicle.Diamond="Diamond" -ENUMS.FormationOld={} -ENUMS.FormationOld.FixedWing={} -ENUMS.FormationOld.FixedWing.LineAbreast=1 -ENUMS.FormationOld.FixedWing.Trail=2 -ENUMS.FormationOld.FixedWing.Wedge=3 -ENUMS.FormationOld.FixedWing.EchelonRight=4 -ENUMS.FormationOld.FixedWing.EchelonLeft=5 -ENUMS.FormationOld.FixedWing.FingerFour=6 -ENUMS.FormationOld.FixedWing.SpreadFour=7 -ENUMS.FormationOld.FixedWing.BomberElement=12 -ENUMS.FormationOld.FixedWing.BomberElementHeight=13 -ENUMS.FormationOld.FixedWing.FighterVic=14 -ENUMS.FormationOld.RotaryWing={} -ENUMS.FormationOld.RotaryWing.Wedge=8 -ENUMS.FormationOld.RotaryWing.Echelon=9 -ENUMS.FormationOld.RotaryWing.Front=10 -ENUMS.FormationOld.RotaryWing.Column=11 -ENUMS.Morse={} -ENUMS.Morse.A="* -" -ENUMS.Morse.B="- * * *" -ENUMS.Morse.C="- * - *" -ENUMS.Morse.D="- * *" -ENUMS.Morse.E="*" -ENUMS.Morse.F="* * - *" -ENUMS.Morse.G="- - *" -ENUMS.Morse.H="* * * *" -ENUMS.Morse.I="* *" -ENUMS.Morse.J="* - - -" -ENUMS.Morse.K="- * -" -ENUMS.Morse.L="* - * *" -ENUMS.Morse.M="- -" -ENUMS.Morse.N="- *" -ENUMS.Morse.O="- - -" -ENUMS.Morse.P="* - - *" -ENUMS.Morse.Q="- - * -" -ENUMS.Morse.R="* - *" -ENUMS.Morse.S="* * *" -ENUMS.Morse.T="-" -ENUMS.Morse.U="* * -" -ENUMS.Morse.V="* * * -" -ENUMS.Morse.W="* - -" -ENUMS.Morse.X="- * * -" -ENUMS.Morse.Y="- * - -" -ENUMS.Morse.Z="- - * *" -ENUMS.Morse.N1="* - - - -" -ENUMS.Morse.N2="* * - - -" -ENUMS.Morse.N3="* * * - -" -ENUMS.Morse.N4="* * * * -" -ENUMS.Morse.N5="* * * * *" -ENUMS.Morse.N6="- * * * *" -ENUMS.Morse.N7="- - * * *" -ENUMS.Morse.N8="- - - * *" -ENUMS.Morse.N9="- - - - *" -ENUMS.Morse.N0="- - - - -" -ENUMS.Morse[" "]=" " -ENUMS.ISOLang= -{ -Arabic='AR', -Chinese='ZH', -English='EN', -French='FR', -German='DE', -Russian='RU', -Spanish='ES', -Japanese='JA', -Italian='IT', -} -ENUMS.Phonetic= -{ -A='Alpha', -B='Bravo', -C='Charlie', -D='Delta', -E='Echo', -F='Foxtrot', -G='Golf', -H='Hotel', -I='India', -J='Juliett', -K='Kilo', -L='Lima', -M='Mike', -N='November', -O='Oscar', -P='Papa', -Q='Quebec', -R='Romeo', -S='Sierra', -T='Tango', -U='Uniform', -V='Victor', -W='Whiskey', -X='Xray', -Y='Yankee', -Z='Zulu', -} -env.setErrorMessageBoxEnabled(false) -routines={} -routines.majorVersion=3 -routines.minorVersion=3 -routines.build=22 -routines.utils={} -routines.utils.round=function(number,decimals) -local power=10^decimals -return math.floor(number*power)/power -end -routines.utils.deepCopy=function(object) -local lookup_table={} -local function _copy(object) -if type(object)~="table"then -return object -elseif lookup_table[object]then -return lookup_table[object] -end -local new_table={} -lookup_table[object]=new_table -for index,value in pairs(object)do -new_table[_copy(index)]=_copy(value) -end -return setmetatable(new_table,getmetatable(object)) -end -local objectreturn=_copy(object) -return objectreturn -end -routines.utils.oneLineSerialize=function(tbl) -lookup_table={} -local function _Serialize(tbl) -if type(tbl)=='table'then -if lookup_table[tbl]then -return lookup_table[object] -end -local tbl_str={} -lookup_table[tbl]=tbl_str -tbl_str[#tbl_str+1]='{' -for ind,val in pairs(tbl)do -local ind_str={} -if type(ind)=="number"then -ind_str[#ind_str+1]='[' -ind_str[#ind_str+1]=tostring(ind) -ind_str[#ind_str+1]=']=' -else -ind_str[#ind_str+1]='[' -ind_str[#ind_str+1]=routines.utils.basicSerialize(ind) -ind_str[#ind_str+1]=']=' -end -local val_str={} -if((type(val)=='number')or(type(val)=='boolean'))then -val_str[#val_str+1]=tostring(val) -val_str[#val_str+1]=',' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -elseif type(val)=='string'then -val_str[#val_str+1]=routines.utils.basicSerialize(val) -val_str[#val_str+1]=',' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -elseif type(val)=='nil'then -val_str[#val_str+1]='nil,' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -elseif type(val)=='table'then -if ind=="__index"then -else -val_str[#val_str+1]=_Serialize(val) -val_str[#val_str+1]=',' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -end -elseif type(val)=='function'then -else -end -end -tbl_str[#tbl_str+1]='}' -return table.concat(tbl_str) -else -if type(tbl)=='string'then -return tbl -else -return tostring(tbl) -end -end -end -local objectreturn=_Serialize(tbl) -return objectreturn -end -routines.utils.basicSerialize=function(s) -if s==nil then -return"\"\"" -else -if((type(s)=='number')or(type(s)=='boolean')or(type(s)=='function')or(type(s)=='table')or(type(s)=='userdata'))then -return tostring(s) -elseif type(s)=='string'then -s=string.format('%s',s:gsub("%%","%%%%")) -return s -end -end -end -routines.utils.toDegree=function(angle) -return angle*180/math.pi -end -routines.utils.toRadian=function(angle) -return angle*math.pi/180 -end -routines.utils.metersToNM=function(meters) -return meters/1852 -end -routines.utils.metersToFeet=function(meters) -return meters/0.3048 -end -routines.utils.NMToMeters=function(NM) -return NM*1852 -end -routines.utils.feetToMeters=function(feet) -return feet*0.3048 -end -routines.utils.mpsToKnots=function(mps) -return mps*3600/1852 -end -routines.utils.mpsToKmph=function(mps) -return mps*3.6 -end -routines.utils.knotsToMps=function(knots) -return knots*1852/3600 -end -routines.utils.kmphToMps=function(kmph) -return kmph/3.6 -end -function routines.utils.makeVec2(Vec3) -if Vec3.z then -return{x=Vec3.x,y=Vec3.z} -else -return{x=Vec3.x,y=Vec3.y} -end -end -function routines.utils.makeVec3(Vec2,y) -if not Vec2.z then -if not y then -y=0 -end -return{x=Vec2.x,y=y,z=Vec2.y} -else -return{x=Vec2.x,y=Vec2.y,z=Vec2.z} -end -end -function routines.utils.makeVec3GL(Vec2,offset) -local adj=offset or 0 -if not Vec2.z then -return{x=Vec2.x,y=(land.getHeight(Vec2)+adj),z=Vec2.y} -else -return{x=Vec2.x,y=(land.getHeight({x=Vec2.x,y=Vec2.z})+adj),z=Vec2.z} -end -end -routines.utils.zoneToVec3=function(zone) -local new={} -if type(zone)=='table'and zone.point then -new.x=zone.point.x -new.y=zone.point.y -new.z=zone.point.z -return new -elseif type(zone)=='string'then -zone=trigger.misc.getZone(zone) -if zone then -new.x=zone.point.x -new.y=zone.point.y -new.z=zone.point.z -return new -end -end -end -function routines.utils.getDir(vec,point) -local dir=math.atan2(vec.z,vec.x) -dir=dir+routines.getNorthCorrection(point) -if dir<0 then -dir=dir+2*math.pi -end -return dir -end -function routines.utils.get2DDist(point1,point2) -point1=routines.utils.makeVec3(point1) -point2=routines.utils.makeVec3(point2) -return routines.vec.mag({x=point1.x-point2.x,y=0,z=point1.z-point2.z}) -end -function routines.utils.get3DDist(point1,point2) -return routines.vec.mag({x=point1.x-point2.x,y=point1.y-point2.y,z=point1.z-point2.z}) -end -routines.vec={} -routines.vec.add=function(vec1,vec2) -return{x=vec1.x+vec2.x,y=vec1.y+vec2.y,z=vec1.z+vec2.z} -end -routines.vec.sub=function(vec1,vec2) -return{x=vec1.x-vec2.x,y=vec1.y-vec2.y,z=vec1.z-vec2.z} -end -routines.vec.scalarMult=function(vec,mult) -return{x=vec.x*mult,y=vec.y*mult,z=vec.z*mult} -end -routines.vec.scalar_mult=routines.vec.scalarMult -routines.vec.dp=function(vec1,vec2) -return vec1.x*vec2.x+vec1.y*vec2.y+vec1.z*vec2.z -end -routines.vec.cp=function(vec1,vec2) -return{x=vec1.y*vec2.z-vec1.z*vec2.y,y=vec1.z*vec2.x-vec1.x*vec2.z,z=vec1.x*vec2.y-vec1.y*vec2.x} -end -routines.vec.mag=function(vec) -return(vec.x^2+vec.y^2+vec.z^2)^0.5 -end -routines.vec.getUnitVec=function(vec) -local mag=routines.vec.mag(vec) -return{x=vec.x/mag,y=vec.y/mag,z=vec.z/mag} -end -routines.vec.rotateVec2=function(vec2,theta) -return{x=vec2.x*math.cos(theta)-vec2.y*math.sin(theta),y=vec2.x*math.sin(theta)+vec2.y*math.cos(theta)} -end -routines.tostringMGRS=function(MGRS,acc) -if acc==0 then -return MGRS.UTMZone..' '..MGRS.MGRSDigraph -else -return MGRS.UTMZone..' '..MGRS.MGRSDigraph..' '..string.format('%0'..acc..'d',routines.utils.round(MGRS.Easting/(10^(5-acc)),0)) -..' '..string.format('%0'..acc..'d',routines.utils.round(MGRS.Northing/(10^(5-acc)),0)) -end -end -routines.tostringLL=function(lat,lon,acc,DMS) -local latHemi,lonHemi -if lat>0 then -latHemi='N' -else -latHemi='S' -end -if lon>0 then -lonHemi='E' -else -lonHemi='W' -end -lat=math.abs(lat) -lon=math.abs(lon) -local latDeg=math.floor(lat) -local latMin=(lat-latDeg)*60 -local lonDeg=math.floor(lon) -local lonMin=(lon-lonDeg)*60 -if DMS then -local oldLatMin=latMin -latMin=math.floor(latMin) -local latSec=routines.utils.round((oldLatMin-latMin)*60,acc) -local oldLonMin=lonMin -lonMin=math.floor(lonMin) -local lonSec=routines.utils.round((oldLonMin-lonMin)*60,acc) -if latSec==60 then -latSec=0 -latMin=latMin+1 -end -if lonSec==60 then -lonSec=0 -lonMin=lonMin+1 -end -local secFrmtStr -if acc<=0 then -secFrmtStr='%02d' -else -local width=3+acc -secFrmtStr='%0'..width..'.'..acc..'f' -end -return string.format('%02d',latDeg)..' '..string.format('%02d',latMin)..'\' '..string.format(secFrmtStr,latSec)..'"'..latHemi..' ' -..string.format('%02d',lonDeg)..' '..string.format('%02d',lonMin)..'\' '..string.format(secFrmtStr,lonSec)..'"'..lonHemi -else -latMin=routines.utils.round(latMin,acc) -lonMin=routines.utils.round(lonMin,acc) -if latMin==60 then -latMin=0 -latDeg=latDeg+1 -end -if lonMin==60 then -lonMin=0 -lonDeg=lonDeg+1 -end -local minFrmtStr -if acc<=0 then -minFrmtStr='%02d' -else -local width=3+acc -minFrmtStr='%0'..width..'.'..acc..'f' -end -return string.format('%02d',latDeg)..' '..string.format(minFrmtStr,latMin)..'\''..latHemi..' ' -..string.format('%02d',lonDeg)..' '..string.format(minFrmtStr,lonMin)..'\''..lonHemi -end -end -routines.tostringBR=function(az,dist,alt,metric) -az=routines.utils.round(routines.utils.toDegree(az),0) -if metric then -dist=routines.utils.round(dist/1000,2) -else -dist=routines.utils.round(routines.utils.metersToNM(dist),2) -end -local s=string.format('%03d',az)..' for '..dist -if alt then -if metric then -s=s..' at '..routines.utils.round(alt,0) -else -s=s..' at '..routines.utils.round(routines.utils.metersToFeet(alt),0) -end -end -return s -end -routines.getNorthCorrection=function(point) -if not point.z then -point.z=point.y -point.y=0 -end -local lat,lon=coord.LOtoLL(point) -local north_posit=coord.LLtoLO(lat+1,lon) -return math.atan2(north_posit.z-point.z,north_posit.x-point.x) -end -do -local idNum=0 -routines.addEventHandler=function(f) -local handler={} -idNum=idNum+1 -handler.id=idNum -handler.f=f -handler.onEvent=function(self,event) -self.f(event) -end -world.addEventHandler(handler) -end -routines.removeEventHandler=function(id) -for key,handler in pairs(world.eventHandlers)do -if handler.id and handler.id==id then -world.eventHandlers[key]=nil -return true -end -end -return false -end -end -function routines.getRandPointInCircle(point,radius,innerRadius) -local theta=2*math.pi*math.random() -local rad=math.random()+math.random() -if rad>1 then -rad=2-rad -end -local radMult -if innerRadius and innerRadius<=radius then -radMult=(radius-innerRadius)*rad+innerRadius -else -radMult=radius*rad -end -if not point.z then -point.z=point.y -end -local rndCoord -if radius>0 then -rndCoord={x=math.cos(theta)*radMult+point.x,y=math.sin(theta)*radMult+point.z} -else -rndCoord={x=point.x,y=point.z} -end -return rndCoord -end -routines.goRoute=function(group,path) -local misTask={ -id='Mission', -params={ -route={ -points=routines.utils.deepCopy(path), -}, -}, -} -if type(group)=='string'then -group=Group.getByName(group) -end -local groupCon=group:getController() -if groupCon then -groupCon:setTask(misTask) -return true -end -Controller.setTask(groupCon,misTask) -return false -end -routines.ground={} -routines.fixedWing={} -routines.heli={} -routines.ground.buildWP=function(point,overRideForm,overRideSpeed) -local wp={} -wp.x=point.x -if point.z then -wp.y=point.z -else -wp.y=point.y -end -local form,speed -if point.speed and not overRideSpeed then -wp.speed=point.speed -elseif type(overRideSpeed)=='number'then -wp.speed=overRideSpeed -else -wp.speed=routines.utils.kmphToMps(20) -end -if point.form and not overRideForm then -form=point.form -else -form=overRideForm -end -if not form then -wp.action='Cone' -else -form=string.lower(form) -if form=='off_road'or form=='off road'then -wp.action='Off Road' -elseif form=='on_road'or form=='on road'then -wp.action='On Road' -elseif form=='rank'or form=='line_abrest'or form=='line abrest'or form=='lineabrest'then -wp.action='Rank' -elseif form=='cone'then -wp.action='Cone' -elseif form=='diamond'then -wp.action='Diamond' -elseif form=='vee'then -wp.action='Vee' -elseif form=='echelon_left'or form=='echelon left'or form=='echelonl'then -wp.action='EchelonL' -elseif form=='echelon_right'or form=='echelon right'or form=='echelonr'then -wp.action='EchelonR' -else -wp.action='Cone' -end -end -wp.type='Turning Point' -return wp -end -routines.fixedWing.buildWP=function(point,WPtype,speed,alt,altType) -local wp={} -wp.x=point.x -if point.z then -wp.y=point.z -else -wp.y=point.y -end -if alt and type(alt)=='number'then -wp.alt=alt -else -wp.alt=2000 -end -if altType then -altType=string.lower(altType) -if altType=='radio'or'agl'then -wp.alt_type='RADIO' -elseif altType=='baro'or'asl'then -wp.alt_type='BARO' -end -else -wp.alt_type='RADIO' -end -if point.speed then -speed=point.speed -end -if point.type then -WPtype=point.type -end -if not speed then -wp.speed=routines.utils.kmphToMps(500) -else -wp.speed=speed -end -if not WPtype then -wp.action='Turning Point' -else -WPtype=string.lower(WPtype) -if WPtype=='flyover'or WPtype=='fly over'or WPtype=='fly_over'then -wp.action='Fly Over Point' -elseif WPtype=='turningpoint'or WPtype=='turning point'or WPtype=='turning_point'then -wp.action='Turning Point' -else -wp.action='Turning Point' -end -end -wp.type='Turning Point' -return wp -end -routines.heli.buildWP=function(point,WPtype,speed,alt,altType) -local wp={} -wp.x=point.x -if point.z then -wp.y=point.z -else -wp.y=point.y -end -if alt and type(alt)=='number'then -wp.alt=alt -else -wp.alt=500 -end -if altType then -altType=string.lower(altType) -if altType=='radio'or'agl'then -wp.alt_type='RADIO' -elseif altType=='baro'or'asl'then -wp.alt_type='BARO' -end -else -wp.alt_type='RADIO' -end -if point.speed then -speed=point.speed -end -if point.type then -WPtype=point.type -end -if not speed then -wp.speed=routines.utils.kmphToMps(200) -else -wp.speed=speed -end -if not WPtype then -wp.action='Turning Point' -else -WPtype=string.lower(WPtype) -if WPtype=='flyover'or WPtype=='fly over'or WPtype=='fly_over'then -wp.action='Fly Over Point' -elseif WPtype=='turningpoint'or WPtype=='turning point'or WPtype=='turning_point'then -wp.action='Turning Point' -else -wp.action='Turning Point' -end -end -wp.type='Turning Point' -return wp -end -routines.groupToRandomPoint=function(vars) -local group=vars.group -local point=vars.point -local radius=vars.radius or 0 -local innerRadius=vars.innerRadius -local form=vars.form or'Cone' -local heading=vars.heading or math.random()*2*math.pi -local headingDegrees=vars.headingDegrees -local speed=vars.speed or routines.utils.kmphToMps(20) -local useRoads -if not vars.disableRoads then -useRoads=true -else -useRoads=false -end -local path={} -if headingDegrees then -heading=headingDegrees*math.pi/180 -end -if heading>=2*math.pi then -heading=heading-2*math.pi -end -local rndCoord=routines.getRandPointInCircle(point,radius,innerRadius) -local offset={} -local posStart=routines.getLeadPos(group) -offset.x=routines.utils.round(math.sin(heading-(math.pi/2))*50+rndCoord.x,3) -offset.z=routines.utils.round(math.cos(heading+(math.pi/2))*50+rndCoord.y,3) -path[#path+1]=routines.ground.buildWP(posStart,form,speed) -if useRoads==true and((point.x-posStart.x)^2+(point.z-posStart.z)^2)^0.5>radius*1.3 then -path[#path+1]=routines.ground.buildWP({['x']=posStart.x+11,['z']=posStart.z+11},'off_road',speed) -path[#path+1]=routines.ground.buildWP(posStart,'on_road',speed) -path[#path+1]=routines.ground.buildWP(offset,'on_road',speed) -else -path[#path+1]=routines.ground.buildWP({['x']=posStart.x+25,['z']=posStart.z+25},form,speed) -end -path[#path+1]=routines.ground.buildWP(offset,form,speed) -path[#path+1]=routines.ground.buildWP(rndCoord,form,speed) -routines.goRoute(group,path) -return -end -routines.groupRandomDistSelf=function(gpData,dist,form,heading,speed) -local pos=routines.getLeadPos(gpData) -local fakeZone={} -fakeZone.radius=dist or math.random(300,1000) -fakeZone.point={x=pos.x,y,pos.y,z=pos.z} -routines.groupToRandomZone(gpData,fakeZone,form,heading,speed) -return -end -routines.groupToRandomZone=function(gpData,zone,form,heading,speed) -if type(gpData)=='string'then -gpData=Group.getByName(gpData) -end -if type(zone)=='string'then -zone=trigger.misc.getZone(zone) -elseif type(zone)=='table'and not zone.radius then -zone=trigger.misc.getZone(zone[math.random(1,#zone)]) -end -if speed then -speed=routines.utils.kmphToMps(speed) -end -local vars={} -vars.group=gpData -vars.radius=zone.radius -vars.form=form -vars.headingDegrees=heading -vars.speed=speed -vars.point=routines.utils.zoneToVec3(zone) -routines.groupToRandomPoint(vars) -return -end -routines.isTerrainValid=function(coord,terrainTypes) -if coord.z then -coord.y=coord.z -end -local typeConverted={} -if type(terrainTypes)=='string'then -for constId,constData in pairs(land.SurfaceType)do -if string.lower(constId)==string.lower(terrainTypes)or string.lower(constData)==string.lower(terrainTypes)then -table.insert(typeConverted,constId) -end -end -elseif type(terrainTypes)=='table'then -for typeId,typeData in pairs(terrainTypes)do -for constId,constData in pairs(land.SurfaceType)do -if string.lower(constId)==string.lower(typeData)or string.lower(constData)==string.lower(typeId)then -table.insert(typeConverted,constId) -end -end -end -end -for validIndex,validData in pairs(typeConverted)do -if land.getSurfaceType(coord)==land.SurfaceType[validData]then -return true -end -end -return false -end -routines.groupToPoint=function(gpData,point,form,heading,speed,useRoads) -if type(point)=='string'then -point=trigger.misc.getZone(point) -end -if speed then -speed=routines.utils.kmphToMps(speed) -end -local vars={} -vars.group=gpData -vars.form=form -vars.headingDegrees=heading -vars.speed=speed -vars.disableRoads=useRoads -vars.point=routines.utils.zoneToVec3(point) -routines.groupToRandomPoint(vars) -return -end -routines.getLeadPos=function(group) -if type(group)=='string'then -group=Group.getByName(group) -end -local units=group:getUnits() -local leader=units[1] -if not leader then -local lowestInd=math.huge -for ind,unit in pairs(units)do -if ind0 then -local maxPos=-math.huge -local maxPosInd -for i=1,#unitPosTbl do -local rotatedVec2=routines.vec.rotateVec2(routines.utils.makeVec2(unitPosTbl[i]),heading) -if(not maxPos)or maxPos=1.0 then -CurrentZoneID=routines.IsUnitInZones(CargoUnit,LandingZones) -if CurrentZoneID then -break -end -end -end -end -return CurrentZoneID -end -function routines.IsUnitInZones(TransportUnit,LandingZones) -local TransportZoneResult=nil -local TransportZonePos=nil -local TransportZone=nil -if TransportUnit then -local TransportUnitPos=TransportUnit:getPosition().p -if type(LandingZones)=="table"then -for LandingZoneID,LandingZoneName in pairs(LandingZones)do -TransportZone=trigger.misc.getZone(LandingZoneName) -if TransportZone then -TransportZonePos={radius=TransportZone.radius,x=TransportZone.point.x,y=TransportZone.point.y,z=TransportZone.point.z} -if(((TransportUnitPos.x-TransportZonePos.x)^2+(TransportUnitPos.z-TransportZonePos.z)^2)^0.5<=TransportZonePos.radius)then -TransportZoneResult=LandingZoneID -break -end -end -end -else -TransportZone=trigger.misc.getZone(LandingZones) -TransportZonePos={radius=TransportZone.radius,x=TransportZone.point.x,y=TransportZone.point.y,z=TransportZone.point.z} -if(((TransportUnitPos.x-TransportZonePos.x)^2+(TransportUnitPos.z-TransportZonePos.z)^2)^0.5<=TransportZonePos.radius)then -TransportZoneResult=1 -end -end -if TransportZoneResult then -else -end -return TransportZoneResult -else -return nil -end -end -function routines.IsUnitNearZonesRadius(TransportUnit,LandingZones,ZoneRadius) -local TransportZoneResult=nil -local TransportZonePos=nil -local TransportZone=nil -if TransportUnit then -local TransportUnitPos=TransportUnit:getPosition().p -if type(LandingZones)=="table"then -for LandingZoneID,LandingZoneName in pairs(LandingZones)do -TransportZone=trigger.misc.getZone(LandingZoneName) -if TransportZone then -TransportZonePos={radius=TransportZone.radius,x=TransportZone.point.x,y=TransportZone.point.y,z=TransportZone.point.z} -if(((TransportUnitPos.x-TransportZonePos.x)^2+(TransportUnitPos.z-TransportZonePos.z)^2)^0.5<=ZoneRadius)then -TransportZoneResult=LandingZoneID -break -end -end -end -else -TransportZone=trigger.misc.getZone(LandingZones) -TransportZonePos={radius=TransportZone.radius,x=TransportZone.point.x,y=TransportZone.point.y,z=TransportZone.point.z} -if(((TransportUnitPos.x-TransportZonePos.x)^2+(TransportUnitPos.z-TransportZonePos.z)^2)^0.5<=ZoneRadius)then -TransportZoneResult=1 -end -end -if TransportZoneResult then -else -end -return TransportZoneResult -else -return nil -end -end -function routines.IsStaticInZones(TransportStatic,LandingZones) -local TransportZoneResult=nil -local TransportZonePos=nil -local TransportZone=nil -local TransportStaticPos=TransportStatic:getPosition().p -if type(LandingZones)=="table"then -for LandingZoneID,LandingZoneName in pairs(LandingZones)do -TransportZone=trigger.misc.getZone(LandingZoneName) -if TransportZone then -TransportZonePos={radius=TransportZone.radius,x=TransportZone.point.x,y=TransportZone.point.y,z=TransportZone.point.z} -if(((TransportStaticPos.x-TransportZonePos.x)^2+(TransportStaticPos.z-TransportZonePos.z)^2)^0.5<=TransportZonePos.radius)then -TransportZoneResult=LandingZoneID -break -end -end -end -else -TransportZone=trigger.misc.getZone(LandingZones) -TransportZonePos={radius=TransportZone.radius,x=TransportZone.point.x,y=TransportZone.point.y,z=TransportZone.point.z} -if(((TransportStaticPos.x-TransportZonePos.x)^2+(TransportStaticPos.z-TransportZonePos.z)^2)^0.5<=TransportZonePos.radius)then -TransportZoneResult=1 -end -end -return TransportZoneResult -end -function routines.IsUnitInRadius(CargoUnit,ReferencePosition,Radius) -local Valid=true -local CargoPos=CargoUnit:getPosition().p -local ReferenceP=ReferencePosition.p -if(((CargoPos.x-ReferenceP.x)^2+(CargoPos.z-ReferenceP.z)^2)^0.5<=Radius)then -else -Valid=false -end -return Valid -end -function routines.IsPartOfGroupInRadius(CargoGroup,ReferencePosition,Radius) -local Valid=true -Valid=routines.ValidateGroup(CargoGroup,"CargoGroup",Valid) -local CargoUnits=CargoGroup:getUnits() -for CargoUnitId,CargoUnit in pairs(CargoUnits)do -local CargoUnitPos=CargoUnit:getPosition().p -local ReferenceP=ReferencePosition.p -if(((CargoUnitPos.x-ReferenceP.x)^2+(CargoUnitPos.z-ReferenceP.z)^2)^0.5<=Radius)then -else -Valid=false -break -end -end -return Valid -end -function routines.ValidateString(Variable,VariableName,Valid) -if type(Variable)=="string"then -if Variable==""then -error("routines.ValidateString: error: "..VariableName.." must be filled out!") -Valid=false -end -else -error("routines.ValidateString: error: "..VariableName.." is not a string.") -Valid=false -end -return Valid -end -function routines.ValidateNumber(Variable,VariableName,Valid) -if type(Variable)=="number"then -else -error("routines.ValidateNumber: error: "..VariableName.." is not a number.") -Valid=false -end -return Valid -end -function routines.ValidateGroup(Variable,VariableName,Valid) -if Variable==nil then -error("routines.ValidateGroup: error: "..VariableName.." is a nil value!") -Valid=false -end -return Valid -end -function routines.ValidateZone(LandingZones,VariableName,Valid) -if LandingZones==nil then -error("routines.ValidateGroup: error: "..VariableName.." is a nil value!") -Valid=false -end -if type(LandingZones)=="table"then -for LandingZoneID,LandingZoneName in pairs(LandingZones)do -if trigger.misc.getZone(LandingZoneName)==nil then -error("routines.ValidateGroup: error: Zone "..LandingZoneName.." does not exist!") -Valid=false -break -end -end -else -if trigger.misc.getZone(LandingZones)==nil then -error("routines.ValidateGroup: error: Zone "..LandingZones.." does not exist!") -Valid=false -end -end -return Valid -end -function routines.ValidateEnumeration(Variable,VariableName,Enum,Valid) -local ValidVariable=false -for EnumId,EnumData in pairs(Enum)do -if Variable==EnumData then -ValidVariable=true -break -end -end -if ValidVariable then -else -error('TransportValidateEnum: " .. VariableName .. " is not a valid type.'..Variable) -Valid=false -end -return Valid -end -function routines.getGroupRoute(groupIdent,task) -local gpId=groupIdent -if type(groupIdent)=='string'and not tonumber(groupIdent)then -gpId=_DATABASE.Templates.Groups[groupIdent].groupId -end -for coa_name,coa_data in pairs(env.mission.coalition)do -if(coa_name=='red'or coa_name=='blue')and type(coa_data)=='table'then -if coa_data.country then -for cntry_id,cntry_data in pairs(coa_data.country)do -for obj_type_name,obj_type_data in pairs(cntry_data)do -if obj_type_name=="helicopter"or obj_type_name=="ship"or obj_type_name=="plane"or obj_type_name=="vehicle"then -if((type(obj_type_data)=='table')and obj_type_data.group and(type(obj_type_data.group)=='table')and(#obj_type_data.group>0))then -for group_num,group_data in pairs(obj_type_data.group)do -if group_data and group_data.groupId==gpId then -if group_data.route and group_data.route.points and#group_data.route.points>0 then -local points={} -for point_num,point in pairs(group_data.route.points)do -local routeData={} -if env.mission.version>7 then -routeData.name=env.getValueDictByKey(point.name) -else -routeData.name=point.name -end -if not point.point then -routeData.x=point.x -routeData.y=point.y -else -routeData.point=point.point -end -routeData.form=point.action -routeData.speed=point.speed -routeData.alt=point.alt -routeData.alt_type=point.alt_type -routeData.airdromeId=point.airdromeId -routeData.helipadId=point.helipadId -routeData.type=point.type -routeData.action=point.action -if task then -routeData.task=point.task -end -points[point_num]=routeData -end -return points -end -return -end -end -end -end -end -end -end -end -end -end -routines.ground.patrolRoute=function(vars) -local tempRoute={} -local useRoute={} -local gpData=vars.gpData -if type(gpData)=='string'then -gpData=Group.getByName(gpData) -end -local useGroupRoute -if not vars.useGroupRoute then -useGroupRoute=vars.gpData -else -useGroupRoute=vars.useGroupRoute -end -local routeProvided=false -if not vars.route then -if useGroupRoute then -tempRoute=routines.getGroupRoute(useGroupRoute) -end -else -useRoute=vars.route -local posStart=routines.getLeadPos(gpData) -useRoute[1]=routines.ground.buildWP(posStart,useRoute[1].action,useRoute[1].speed) -routeProvided=true -end -local overRideSpeed=vars.speed or'default' -local pType=vars.pType -local offRoadForm=vars.offRoadForm or'default' -local onRoadForm=vars.onRoadForm or'default' -if routeProvided==false and#tempRoute>0 then -local posStart=routines.getLeadPos(gpData) -useRoute[#useRoute+1]=routines.ground.buildWP(posStart,offRoadForm,overRideSpeed) -for i=1,#tempRoute do -local tempForm=tempRoute[i].action -local tempSpeed=tempRoute[i].speed -if offRoadForm=='default'then -tempForm=tempRoute[i].action -end -if onRoadForm=='default'then -onRoadForm='On Road' -end -if(string.lower(tempRoute[i].action)=='on road'or string.lower(tempRoute[i].action)=='onroad'or string.lower(tempRoute[i].action)=='on_road')then -tempForm=onRoadForm -else -tempForm=offRoadForm -end -if type(overRideSpeed)=='number'then -tempSpeed=overRideSpeed -end -useRoute[#useRoute+1]=routines.ground.buildWP(tempRoute[i],tempForm,tempSpeed) -end -if pType and string.lower(pType)=='doubleback'then -local curRoute=routines.utils.deepCopy(useRoute) -for i=#curRoute,2,-1 do -useRoute[#useRoute+1]=routines.ground.buildWP(curRoute[i],curRoute[i].action,curRoute[i].speed) -end -end -useRoute[1].action=useRoute[#useRoute].action -end -local cTask3={} -local newPatrol={} -newPatrol.route=useRoute -newPatrol.gpData=gpData:getName() -cTask3[#cTask3+1]='routines.ground.patrolRoute(' -cTask3[#cTask3+1]=routines.utils.oneLineSerialize(newPatrol) -cTask3[#cTask3+1]=')' -cTask3=table.concat(cTask3) -local tempTask={ -id='WrappedAction', -params={ -action={ -id='Script', -params={ -command=cTask3, -}, -}, -}, -} -useRoute[#useRoute].task=tempTask -routines.goRoute(gpData,useRoute) -return -end -routines.ground.patrol=function(gpData,pType,form,speed) -local vars={} -if type(gpData)=='table'and gpData:getName()then -gpData=gpData:getName() -end -vars.useGroupRoute=gpData -vars.gpData=gpData -vars.pType=pType -vars.offRoadForm=form -vars.speed=speed -routines.ground.patrolRoute(vars) -return -end -function routines.GetUnitHeight(CheckUnit) -local UnitPoint=CheckUnit:getPoint() -local UnitPosition={x=UnitPoint.x,y=UnitPoint.z} -local UnitHeight=UnitPoint.y -local LandHeight=land.getHeight(UnitPosition) -return UnitHeight-LandHeight -end -Su34Status={status={}} -boardMsgRed={statusMsg=""} -boardMsgAll={timeMsg=""} -SpawnSettings={} -Su34MenuPath={} -Su34Menus=0 -function Su34AttackCarlVinson(groupName) -local groupSu34=Group.getByName(groupName) -local controllerSu34=groupSu34.getController(groupSu34) -local groupCarlVinson=Group.getByName("US Carl Vinson #001") -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.OPEN_FIRE) -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE) -if groupCarlVinson~=nil then -controllerSu34.pushTask(controllerSu34,{id='AttackGroup',params={groupId=groupCarlVinson:getID(),expend=AI.Task.WeaponExpend.ALL,attackQtyLimit=true}}) -end -Su34Status.status[groupName]=1 -MessageToRed(string.format('%s: ',groupName)..'Attacking carrier Carl Vinson. ',10,'RedStatus'..groupName) -end -function Su34AttackWest(groupName) -local groupSu34=Group.getByName(groupName) -local controllerSu34=groupSu34.getController(groupSu34) -local groupShipWest1=Group.getByName("US Ship West #001") -local groupShipWest2=Group.getByName("US Ship West #002") -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.OPEN_FIRE) -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE) -if groupShipWest1~=nil then -controllerSu34.pushTask(controllerSu34,{id='AttackGroup',params={groupId=groupShipWest1:getID(),expend=AI.Task.WeaponExpend.ALL,attackQtyLimit=true}}) -end -if groupShipWest2~=nil then -controllerSu34.pushTask(controllerSu34,{id='AttackGroup',params={groupId=groupShipWest2:getID(),expend=AI.Task.WeaponExpend.ALL,attackQtyLimit=true}}) -end -Su34Status.status[groupName]=2 -MessageToRed(string.format('%s: ',groupName)..'Attacking invading ships in the west. ',10,'RedStatus'..groupName) -end -function Su34AttackNorth(groupName) -local groupSu34=Group.getByName(groupName) -local controllerSu34=groupSu34.getController(groupSu34) -local groupShipNorth1=Group.getByName("US Ship North #001") -local groupShipNorth2=Group.getByName("US Ship North #002") -local groupShipNorth3=Group.getByName("US Ship North #003") -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.OPEN_FIRE) -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE) -if groupShipNorth1~=nil then -controllerSu34.pushTask(controllerSu34,{id='AttackGroup',params={groupId=groupShipNorth1:getID(),expend=AI.Task.WeaponExpend.ALL,attackQtyLimit=false}}) -end -if groupShipNorth2~=nil then -controllerSu34.pushTask(controllerSu34,{id='AttackGroup',params={groupId=groupShipNorth2:getID(),expend=AI.Task.WeaponExpend.ALL,attackQtyLimit=false}}) -end -if groupShipNorth3~=nil then -controllerSu34.pushTask(controllerSu34,{id='AttackGroup',params={groupId=groupShipNorth3:getID(),expend=AI.Task.WeaponExpend.ALL,attackQtyLimit=false}}) -end -Su34Status.status[groupName]=3 -MessageToRed(string.format('%s: ',groupName)..'Attacking invading ships in the north. ',10,'RedStatus'..groupName) -end -function Su34Orbit(groupName) -local groupSu34=Group.getByName(groupName) -local controllerSu34=groupSu34:getController() -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.WEAPON_HOLD) -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE) -controllerSu34:pushTask({id='ControlledTask',params={task={id='Orbit',params={pattern=AI.Task.OrbitPattern.RACE_TRACK}},stopCondition={duration=600}}}) -Su34Status.status[groupName]=4 -MessageToRed(string.format('%s: ',groupName)..'In orbit and awaiting further instructions. ',10,'RedStatus'..groupName) -end -function Su34TakeOff(groupName) -local groupSu34=Group.getByName(groupName) -local controllerSu34=groupSu34:getController() -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.WEAPON_HOLD) -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE) -Su34Status.status[groupName]=8 -MessageToRed(string.format('%s: ',groupName)..'Take-Off. ',10,'RedStatus'..groupName) -end -function Su34Hold(groupName) -local groupSu34=Group.getByName(groupName) -local controllerSu34=groupSu34:getController() -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.WEAPON_HOLD) -controllerSu34.setOption(controllerSu34,AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE) -Su34Status.status[groupName]=5 -MessageToRed(string.format('%s: ',groupName)..'Holding Weapons. ',10,'RedStatus'..groupName) -end -function Su34RTB(groupName) -Su34Status.status[groupName]=6 -MessageToRed(string.format('%s: ',groupName)..'Return to Krasnodar. ',10,'RedStatus'..groupName) -end -function Su34Destroyed(groupName) -Su34Status.status[groupName]=7 -MessageToRed(string.format('%s: ',groupName)..'Destroyed. ',30,'RedStatus'..groupName) -end -function GroupAlive(groupName) -local groupTest=Group.getByName(groupName) -local groupExists=false -if groupTest then -groupExists=groupTest:isExist() -end -return groupExists -end -function Su34IsDead() -end -function Su34OverviewStatus() -local msg="" -local currentStatus=0 -local Exists=false -for groupName,currentStatus in pairs(Su34Status.status)do -env.info(('Su34 Overview Status: GroupName = '..groupName)) -Alive=GroupAlive(groupName) -if Alive then -if currentStatus==1 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Attacking carrier Carl Vinson. " -elseif currentStatus==2 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Attacking supporting ships in the west. " -elseif currentStatus==3 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Attacking invading ships in the north. " -elseif currentStatus==4 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."In orbit and awaiting further instructions. " -elseif currentStatus==5 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Holding Weapons. " -elseif currentStatus==6 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Return to Krasnodar. " -elseif currentStatus==7 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Destroyed. " -elseif currentStatus==8 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Take-Off. " -end -else -if currentStatus==7 then -msg=msg..string.format("%s: ",groupName) -msg=msg.."Destroyed. " -else -Su34Destroyed(groupName) -end -end -end -boardMsgRed.statusMsg=msg -end -function UpdateBoardMsg() -Su34OverviewStatus() -MessageToRed(boardMsgRed.statusMsg,15,'RedStatus') -end -function MusicReset(flg) -trigger.action.setUserFlag(95,flg) -end -function PlaneActivate(groupNameFormat,flg) -local groupName=groupNameFormat..string.format("#%03d",trigger.misc.getUserFlag(flg)) -trigger.action.activateGroup(Group.getByName(groupName)) -end -function Su34Menu(groupName) -local groupSu34=Group.getByName(groupName) -if Su34Status.status[groupName]==1 or -Su34Status.status[groupName]==2 or -Su34Status.status[groupName]==3 or -Su34Status.status[groupName]==4 or -Su34Status.status[groupName]==5 then -if Su34MenuPath[groupName]==nil then -if planeMenuPath==nil then -planeMenuPath=missionCommands.addSubMenuForCoalition( -coalition.side.RED, -"SU-34 anti-ship flights", -nil -) -end -Su34MenuPath[groupName]=missionCommands.addSubMenuForCoalition( -coalition.side.RED, -"Flight "..groupName, -planeMenuPath -) -missionCommands.addCommandForCoalition( -coalition.side.RED, -"Attack carrier Carl Vinson", -Su34MenuPath[groupName], -Su34AttackCarlVinson, -groupName -) -missionCommands.addCommandForCoalition( -coalition.side.RED, -"Attack ships in the west", -Su34MenuPath[groupName], -Su34AttackWest, -groupName -) -missionCommands.addCommandForCoalition( -coalition.side.RED, -"Attack ships in the north", -Su34MenuPath[groupName], -Su34AttackNorth, -groupName -) -missionCommands.addCommandForCoalition( -coalition.side.RED, -"Hold position and await instructions", -Su34MenuPath[groupName], -Su34Orbit, -groupName -) -missionCommands.addCommandForCoalition( -coalition.side.RED, -"Report status", -Su34MenuPath[groupName], -Su34OverviewStatus -) -end -else -if Su34MenuPath[groupName]then -missionCommands.removeItemForCoalition(coalition.side.RED,Su34MenuPath[groupName]) -end -end -end -function ChooseInfantry(TeleportPrefixTable,TeleportMax) -TeleportPrefixTableCount=#TeleportPrefixTable -TeleportPrefixTableIndex=math.random(1,TeleportPrefixTableCount) -local TeleportFound=false -local TeleportLoop=true -local Index=TeleportPrefixTableIndex -local TeleportPrefix='' -while TeleportLoop do -TeleportPrefix=TeleportPrefixTable[Index] -if SpawnSettings[TeleportPrefix]then -if SpawnSettings[TeleportPrefix]['SpawnCount']-10 then -local PlayerFound=false -local MusicStart=0 -local MusicTime=0 -for SndQueueIdx,SndQueue in pairs(_MusicTable.Queue)do -if SndQueue.PlayerName==PlayerName then -PlayerFound=true -MusicStart=SndQueue.Start -MusicTime=_MusicTable.Files[SndQueue.Ref].Time -break -end -end -if PlayerFound then -if MusicStart+MusicTime<=timer.getTime()then -MusicOut=true -end -else -MusicOut=true -end -end -if MusicOut then -else -end -return MusicOut -end -function MusicScheduler() -if _MusicTable['Queue']~=nil and _MusicTable.FileCnt>0 then -for SndQueueIdx,SndQueue in pairs(_MusicTable.Queue)do -if SndQueue.Continue then -if MusicCanStart(SndQueue.PlayerName)then -MusicToPlayer('',SndQueue.PlayerName,true) -end -end -end -end -end -env.info(('Init: Scripts Loaded v1.1')) -SMOKECOLOR=trigger.smokeColor -FLARECOLOR=trigger.flareColor -BIGSMOKEPRESET={ -SmallSmokeAndFire=1, -MediumSmokeAndFire=2, -LargeSmokeAndFire=3, -HugeSmokeAndFire=4, -SmallSmoke=5, -MediumSmoke=6, -LargeSmoke=7, -HugeSmoke=8, -} -DCSMAP={ -Caucasus="Caucasus", -NTTR="Nevada", -Normandy="Normandy", -PersianGulf="PersianGulf", -TheChannel="TheChannel", -Syria="Syria", -} -CALLSIGN={ -Aircraft={ -Enfield=1, -Springfield=2, -Uzi=3, -Colt=4, -Dodge=5, -Ford=6, -Chevy=7, -Pontiac=8, -Hawg=9, -Boar=10, -Pig=11, -Tusk=12, -}, -AWACS={ -Overlord=1, -Magic=2, -Wizard=3, -Focus=4, -Darkstar=5, -}, -Tanker={ -Texaco=1, -Arco=2, -Shell=3, -}, -JTAC={ -Axeman=1, -Darknight=2, -Warrior=3, -Pointer=4, -Eyeball=5, -Moonbeam=6, -Whiplash=7, -Finger=8, -Pinpoint=9, -Ferret=10, -Shaba=11, -Playboy=12, -Hammer=13, -Jaguar=14, -Deathstar=15, -Anvil=16, -Firefly=17, -Mantis=18, -Badger=19, -}, -FARP={ -London=1, -Dallas=2, -Paris=3, -Moscow=4, -Berlin=5, -Rome=6, -Madrid=7, -Warsaw=8, -Dublin=9, -Perth=10, -}, -} -UTILS={ -_MarkID=1 -} -UTILS.IsInstanceOf=function(object,className) -if not type(className)=='string'then -if type(className)=='table'and className.IsInstanceOf~=nil then -className=className.ClassName -else -local err_str='className parameter should be a string; parameter received: '..type(className) -return false -end -end -if type(object)=='table'and object.IsInstanceOf~=nil then -return object:IsInstanceOf(className) -else -local basicDataTypes={'string','number','function','boolean','nil','table'} -for _,basicDataType in ipairs(basicDataTypes)do -if className==basicDataType then -return type(object)==basicDataType -end -end -end -return false -end -UTILS.DeepCopy=function(object) -local lookup_table={} -local function _copy(object) -if type(object)~="table"then -return object -elseif lookup_table[object]then -return lookup_table[object] -end -local new_table={} -lookup_table[object]=new_table -for index,value in pairs(object)do -new_table[_copy(index)]=_copy(value) -end -return setmetatable(new_table,getmetatable(object)) -end -local objectreturn=_copy(object) -return objectreturn -end -UTILS.OneLineSerialize=function(tbl) -lookup_table={} -local function _Serialize(tbl) -if type(tbl)=='table'then -if lookup_table[tbl]then -return lookup_table[object] -end -local tbl_str={} -lookup_table[tbl]=tbl_str -tbl_str[#tbl_str+1]='{' -for ind,val in pairs(tbl)do -local ind_str={} -if type(ind)=="number"then -ind_str[#ind_str+1]='[' -ind_str[#ind_str+1]=tostring(ind) -ind_str[#ind_str+1]=']=' -else -ind_str[#ind_str+1]='[' -ind_str[#ind_str+1]=routines.utils.basicSerialize(ind) -ind_str[#ind_str+1]=']=' -end -local val_str={} -if((type(val)=='number')or(type(val)=='boolean'))then -val_str[#val_str+1]=tostring(val) -val_str[#val_str+1]=',' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -elseif type(val)=='string'then -val_str[#val_str+1]=routines.utils.basicSerialize(val) -val_str[#val_str+1]=',' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -elseif type(val)=='nil'then -val_str[#val_str+1]='nil,' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -elseif type(val)=='table'then -if ind=="__index"then -else -val_str[#val_str+1]=_Serialize(val) -val_str[#val_str+1]=',' -tbl_str[#tbl_str+1]=table.concat(ind_str) -tbl_str[#tbl_str+1]=table.concat(val_str) -end -elseif type(val)=='function'then -tbl_str[#tbl_str+1]="f() "..tostring(ind) -tbl_str[#tbl_str+1]=',' -else -env.info('unable to serialize value type '..routines.utils.basicSerialize(type(val))..' at index '..tostring(ind)) -env.info(debug.traceback()) -end -end -tbl_str[#tbl_str+1]='}' -return table.concat(tbl_str) -else -return tostring(tbl) -end -end -local objectreturn=_Serialize(tbl) -return objectreturn -end -UTILS.BasicSerialize=function(s) -if s==nil then -return"\"\"" -else -if((type(s)=='number')or(type(s)=='boolean')or(type(s)=='function')or(type(s)=='table')or(type(s)=='userdata'))then -return tostring(s) -elseif type(s)=='string'then -s=string.format('%q',s) -return s -end -end -end -UTILS.ToDegree=function(angle) -return angle*180/math.pi -end -UTILS.ToRadian=function(angle) -return angle*math.pi/180 -end -UTILS.MetersToNM=function(meters) -return meters/1852 -end -UTILS.KiloMetersToNM=function(kilometers) -return kilometers/1852*1000 -end -UTILS.MetersToSM=function(meters) -return meters/1609.34 -end -UTILS.KiloMetersToSM=function(kilometers) -return kilometers/1609.34*1000 -end -UTILS.MetersToFeet=function(meters) -return meters/0.3048 -end -UTILS.KiloMetersToFeet=function(kilometers) -return kilometers/0.3048*1000 -end -UTILS.NMToMeters=function(NM) -return NM*1852 -end -UTILS.NMToKiloMeters=function(NM) -return NM*1852/1000 -end -UTILS.FeetToMeters=function(feet) -return feet*0.3048 -end -UTILS.KnotsToKmph=function(knots) -return knots*1.852 -end -UTILS.KmphToKnots=function(knots) -return knots/1.852 -end -UTILS.KmphToMps=function(kmph) -return kmph/3.6 -end -UTILS.MpsToKmph=function(mps) -return mps*3.6 -end -UTILS.MiphToMps=function(miph) -return miph*0.44704 -end -UTILS.MpsToMiph=function(mps) -return mps/0.44704 -end -UTILS.MpsToKnots=function(mps) -return mps*1.94384 -end -UTILS.KnotsToMps=function(knots) -return knots/1.94384 -end -UTILS.CelciusToFarenheit=function(Celcius) -return Celcius*9/5+32 -end -UTILS.hPa2inHg=function(hPa) -return hPa*0.0295299830714 -end -UTILS.KnotsToAltKIAS=function(knots,altitude) -return(knots*0.018*(altitude/1000))+knots -end -UTILS.hPa2mmHg=function(hPa) -return hPa*0.7500615613030 -end -UTILS.kg2lbs=function(kg) -return kg*2.20462 -end -UTILS.tostringLL=function(lat,lon,acc,DMS) -local latHemi,lonHemi -if lat>0 then -latHemi='N' -else -latHemi='S' -end -if lon>0 then -lonHemi='E' -else -lonHemi='W' -end -lat=math.abs(lat) -lon=math.abs(lon) -local latDeg=math.floor(lat) -local latMin=(lat-latDeg)*60 -local lonDeg=math.floor(lon) -local lonMin=(lon-lonDeg)*60 -if DMS then -local oldLatMin=latMin -latMin=math.floor(latMin) -local latSec=UTILS.Round((oldLatMin-latMin)*60,acc) -local oldLonMin=lonMin -lonMin=math.floor(lonMin) -local lonSec=UTILS.Round((oldLonMin-lonMin)*60,acc) -if latSec==60 then -latSec=0 -latMin=latMin+1 -end -if lonSec==60 then -lonSec=0 -lonMin=lonMin+1 -end -local secFrmtStr -secFrmtStr='%02d' -if acc<=0 then -secFrmtStr='%02d' -else -local width=3+acc -secFrmtStr='%0'..width..'.'..acc..'f' -end -return string.format('%03d°',latDeg)..string.format('%02d',latMin)..'\''..string.format(secFrmtStr,latSec)..'"'..latHemi..' ' -..string.format('%03d°',lonDeg)..string.format('%02d',lonMin)..'\''..string.format(secFrmtStr,lonSec)..'"'..lonHemi -else -latMin=UTILS.Round(latMin,acc) -lonMin=UTILS.Round(lonMin,acc) -if latMin==60 then -latMin=0 -latDeg=latDeg+1 -end -if lonMin==60 then -lonMin=0 -lonDeg=lonDeg+1 -end -local minFrmtStr -if acc<=0 then -minFrmtStr='%02d' -else -local width=3+acc -minFrmtStr='%0'..width..'.'..acc..'f' -end -return string.format('%03d°',latDeg)..' '..string.format(minFrmtStr,latMin)..'\''..latHemi..' ' -..string.format('%03d°',lonDeg)..' '..string.format(minFrmtStr,lonMin)..'\''..lonHemi -end -end -UTILS.tostringMGRS=function(MGRS,acc) -if acc==0 then -return MGRS.UTMZone..' '..MGRS.MGRSDigraph -else -local Easting=tostring(MGRS.Easting) -local Northing=tostring(MGRS.Northing) -local nE=5-string.len(Easting) -local nN=5-string.len(Northing) -for i=1,nE do Easting="0"..Easting end -for i=1,nN do Northing="0"..Northing end -return string.format("%s %s %s %s",MGRS.UTMZone,MGRS.MGRSDigraph,string.sub(Easting,1,acc),string.sub(Northing,1,acc)) -end -end -function UTILS.Round(num,idp) -local mult=10^(idp or 0) -return math.floor(num*mult+0.5)/mult -end -function UTILS.DoString(s) -local f,err=loadstring(s) -if f then -return true,f() -else -return false,err -end -end -function UTILS.spairs(t,order) -local keys={} -for k in pairs(t)do keys[#keys+1]=k end -if order then -table.sort(keys,function(a,b)return order(t,a,b)end) -else -table.sort(keys) -end -local i=0 -return function() -i=i+1 -if keys[i]then -return keys[i],t[keys[i]] -end -end -end -function UTILS.kpairs(t,getkey,order) -local keys={} -local keyso={} -for k,o in pairs(t)do keys[#keys+1]=k keyso[#keyso+1]=getkey(o)end -if order then -table.sort(keys,function(a,b)return order(t,a,b)end) -else -table.sort(keys) -end -local i=0 -return function() -i=i+1 -if keys[i]then -return keyso[i],t[keys[i]] -end -end -end -function UTILS.rpairs(t) -local keys={} -for k in pairs(t)do keys[#keys+1]=k end -local random={} -local j=#keys -for i=1,j do -local k=math.random(1,#keys) -random[i]=keys[k] -table.remove(keys,k) -end -local i=0 -return function() -i=i+1 -if random[i]then -return random[i],t[random[i]] -end -end -end -function UTILS.GetMarkID() -UTILS._MarkID=UTILS._MarkID+1 -return UTILS._MarkID -end -function UTILS.IsInRadius(InVec2,Vec2,Radius) -local InRadius=((InVec2.x-Vec2.x)^2+(InVec2.y-Vec2.y)^2)^0.5<=Radius -return InRadius -end -function UTILS.IsInSphere(InVec3,Vec3,Radius) -local InSphere=((InVec3.x-Vec3.x)^2+(InVec3.y-Vec3.y)^2+(InVec3.z-Vec3.z)^2)^0.5<=Radius -return InSphere -end -function UTILS.BeaufortScale(speed) -local bn=nil -local bd=nil -if speed<0.51 then -bn=0 -bd="Calm" -elseif speed<2.06 then -bn=1 -bd="Light Air" -elseif speed<3.60 then -bn=2 -bd="Light Breeze" -elseif speed<5.66 then -bn=3 -bd="Gentle Breeze" -elseif speed<8.23 then -bn=4 -bd="Moderate Breeze" -elseif speed<11.32 then -bn=5 -bd="Fresh Breeze" -elseif speed<14.40 then -bn=6 -bd="Strong Breeze" -elseif speed<17.49 then -bn=7 -bd="Moderate Gale" -elseif speed<21.09 then -bn=8 -bd="Fresh Gale" -elseif speed<24.69 then -bn=9 -bd="Strong Gale" -elseif speed<28.81 then -bn=10 -bd="Storm" -elseif speed<32.92 then -bn=11 -bd="Violent Storm" -else -bn=12 -bd="Hurricane" -end -return bn,bd -end -function UTILS.Split(str,sep) -local result={} -local regex=("([^%s]+)"):format(sep) -for each in str:gmatch(regex)do -table.insert(result,each) -end -return result -end -function UTILS.SecondsToClock(seconds,short) -if seconds==nil then -return nil -end -local seconds=tonumber(seconds) -local _seconds=seconds%(60*60*24) -if seconds<0 then -return nil -else -local hours=string.format("%02.f",math.floor(_seconds/3600)) -local mins=string.format("%02.f",math.floor(_seconds/60-(hours*60))) -local secs=string.format("%02.f",math.floor(_seconds-hours*3600-mins*60)) -local days=string.format("%d",seconds/(60*60*24)) -local clock=hours..":"..mins..":"..secs.."+"..days -if short then -if hours=="00"then -clock=hours..":"..mins..":"..secs -else -clock=hours..":"..mins..":"..secs -end -end -return clock -end -end -function UTILS.SecondsOfToday() -local time=timer.getAbsTime() -local clock=UTILS.SecondsToClock(time,true) -return UTILS.ClockToSeconds(clock) -end -function UTILS.SecondsToMidnight() -return 24*60*60-UTILS.SecondsOfToday() -end -function UTILS.ClockToSeconds(clock) -if clock==nil then -return nil -end -local seconds=0 -local dsplit=UTILS.Split(clock,"+") -if#dsplit>1 then -seconds=seconds+tonumber(dsplit[2])*60*60*24 -end -local tsplit=UTILS.Split(dsplit[1],":") -local i=1 -for _,time in ipairs(tsplit)do -if i==1 then -seconds=seconds+tonumber(time)*60*60 -elseif i==2 then -seconds=seconds+tonumber(time)*60 -elseif i==3 then -seconds=seconds+tonumber(time) -end -i=i+1 -end -return seconds -end -function UTILS.DisplayMissionTime(duration) -duration=duration or 5 -local Tnow=timer.getAbsTime() -local mission_time=Tnow-timer.getTime0() -local mission_time_minutes=mission_time/60 -local mission_time_seconds=mission_time%60 -local local_time=UTILS.SecondsToClock(Tnow) -local text=string.format("Time: %s - %02d:%02d",local_time,mission_time_minutes,mission_time_seconds) -MESSAGE:New(text,duration):ToAll() -end -function UTILS.ReplaceIllegalCharacters(Text,ReplaceBy) -ReplaceBy=ReplaceBy or"_" -local text=Text:gsub("[<>|/?*:\\]",ReplaceBy) -return text -end -function UTILS.RandomGaussian(x0,sigma,xmin,xmax,imax) -sigma=sigma or 10 -imax=imax or 100 -local r -local gotit=false -local i=0 -while not gotit do -local x1=math.random() -local x2=math.random() -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>imax then -gotit=true -end -end -return r -end -function UTILS.Randomize(value,fac,lower,upper) -local min -if lower then -min=math.max(value-value*fac,lower) -else -min=value-value*fac -end -local max -if upper then -max=math.min(value+value*fac,upper) -else -max=value+value*fac -end -local r=math.random(min,max) -return r -end -function UTILS.VecDot(a,b) -return a.x*b.x+a.y*b.y+a.z*b.z -end -function UTILS.VecNorm(a) -return math.sqrt(UTILS.VecDot(a,a)) -end -function UTILS.VecDist2D(a,b) -local c={x=b.x-a.x,y=b.y-a.y} -local d=math.sqrt(c.x*c.x+c.y*c.y) -return d -end -function UTILS.VecDist3D(a,b) -local c={x=b.x-a.x,y=b.y-a.y,z=b.z-a.z} -local d=math.sqrt(UTILS.VecDot(c,c)) -return d -end -function UTILS.VecCross(a,b) -return{x=a.y*b.z-a.z*b.y,y=a.z*b.x-a.x*b.z,z=a.x*b.y-a.y*b.x} -end -function UTILS.VecSubstract(a,b) -return{x=a.x-b.x,y=a.y-b.y,z=a.z-b.z} -end -function UTILS.VecAdd(a,b) -return{x=a.x+b.x,y=a.y+b.y,z=a.z+b.z} -end -function UTILS.VecAngle(a,b) -local cosalpha=UTILS.VecDot(a,b)/(UTILS.VecNorm(a)*UTILS.VecNorm(b)) -local alpha=0 -if cosalpha>=0.9999999999 then -alpha=0 -elseif cosalpha<=-0.999999999 then -alpha=math.pi -else -alpha=math.acos(cosalpha) -end -return math.deg(alpha) -end -function UTILS.VecHdg(a) -local h=math.deg(math.atan2(a.z,a.x)) -if h<0 then -h=h+360 -end -return h -end -function UTILS.HdgDiff(h1,h2) -local alpha=math.rad(tonumber(h1)) -local beta=math.rad(tonumber(h2)) -local v1={x=math.cos(alpha),y=0,z=math.sin(alpha)} -local v2={x=math.cos(beta),y=0,z=math.sin(beta)} -local delta=UTILS.VecAngle(v1,v2) -return math.abs(delta) -end -function UTILS.VecTranslate(a,distance,angle) -local SX=a.x -local SY=a.z -local Radians=math.rad(angle or 0) -local TX=distance*math.cos(Radians)+SX -local TY=distance*math.sin(Radians)+SY -return{x=TX,y=a.y,z=TY} -end -function UTILS.Rotate2D(a,angle) -local phi=math.rad(angle) -local x=a.z -local y=a.x -local Z=x*math.cos(phi)-y*math.sin(phi) -local X=x*math.sin(phi)+y*math.cos(phi) -local Y=a.y -local A={x=X,y=Y,z=Z} -return A -end -function UTILS.TACANToFrequency(TACANChannel,TACANMode) -if type(TACANChannel)~="number"then -return nil -end -if TACANMode~="X"and TACANMode~="Y"then -return nil -end -local A=1151 -local B=64 -if TACANChannel<64 then -B=1 -end -if TACANMode=='Y'then -A=1025 -if TACANChannel<64 then -A=1088 -end -else -if TACANChannel<64 then -A=962 -end -end -return(A+TACANChannel-B)*1000000 -end -function UTILS.GetDCSMap() -return env.mission.theatre -end -function UTILS.GetDCSMissionDate() -local year=tostring(env.mission.date.Year) -local month=tostring(env.mission.date.Month) -local day=tostring(env.mission.date.Day) -return string.format("%s/%s/%s",year,month,day),tonumber(year),tonumber(month),tonumber(day) -end -function UTILS.GetMissionDay(Time) -Time=Time or timer.getAbsTime() -local clock=UTILS.SecondsToClock(Time,false) -local x=tonumber(UTILS.Split(clock,"+")[2]) -return x -end -function UTILS.GetMissionDayOfYear(Time) -local Date,Year,Month,Day=UTILS.GetDCSMissionDate() -local d=UTILS.GetMissionDay(Time) -return UTILS.GetDayOfYear(Year,Month,Day)+d -end -function UTILS.GetDate() -local date,year,month,day=UTILS.GetDCSMissionDate() -local time=timer.getAbsTime() -local clock=UTILS.SecondsToClock(time,false) -local x=tonumber(UTILS.Split(clock,"+")[2]) -local day=day+x -end -function UTILS.GetMagneticDeclination(map) -map=map or UTILS.GetDCSMap() -local declination=0 -if map==DCSMAP.Caucasus then -declination=6 -elseif map==DCSMAP.NTTR then -declination=12 -elseif map==DCSMAP.Normandy then -declination=-10 -elseif map==DCSMAP.PersianGulf then -declination=2 -elseif map==DCSMAP.TheChannel then -declination=-10 -elseif map==DCSMAP.Syria then -declination=5 -else -declination=0 -end -return declination -end -function UTILS.FileExists(file) -if io then -local f=io.open(file,"r") -if f~=nil then -io.close(f) -return true -else -return false -end -else -return nil -end -end -function UTILS.CheckMemory(output) -local time=timer.getTime() -local clock=UTILS.SecondsToClock(time) -local mem=collectgarbage("count") -if output then -env.info(string.format("T=%s Memory usage %d kByte = %.2f MByte",clock,mem,mem/1024)) -end -return mem -end -function UTILS.GetCoalitionName(Coalition) -if Coalition then -if Coalition==coalition.side.BLUE then -return"Blue" -elseif Coalition==coalition.side.RED then -return"Red" -elseif Coalition==coalition.side.NEUTRAL then -return"Neutral" -else -return"Unknown" -end -else -return"Unknown" -end -end -function UTILS.GetModulationName(Modulation) -if Modulation then -if Modulation==0 then -return"AM" -elseif Modulation==1 then -return"FM" -else -return"Unknown" -end -else -return"Unknown" -end -end -function UTILS.GetCallsignName(Callsign) -for name,value in pairs(CALLSIGN.Aircraft)do -if value==Callsign then -return name -end -end -for name,value in pairs(CALLSIGN.AWACS)do -if value==Callsign then -return name -end -end -for name,value in pairs(CALLSIGN.JTAC)do -if value==Callsign then -return name -end -end -for name,value in pairs(CALLSIGN.Tanker)do -if value==Callsign then -return name -end -end -return"Ghostrider" -end -function UTILS.GMTToLocalTimeDifference() -local theatre=UTILS.GetDCSMap() -if theatre==DCSMAP.Caucasus then -return 4 -elseif theatre==DCSMAP.PersianGulf then -return 4 -elseif theatre==DCSMAP.NTTR then -return-8 -elseif theatre==DCSMAP.Normandy then -return 0 -elseif theatre==DCSMAP.TheChannel then -return 2 -elseif theatre==DCSMAP.Syria then -return 3 -else -BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0",tostring(theatre))) -return 0 -end -end -function UTILS.GetDayOfYear(Year,Month,Day) -local floor=math.floor -local n1=floor(275*Month/9) -local n2=floor((Month+9)/12) -local n3=(1+floor((Year-4*floor(Year/4)+2)/3)) -return n1-(n2*n3)+Day-30 -end -function UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,Rising,Tlocal) -local zenith=90.83 -local latitude=Latitude -local longitude=Longitude -local rising=Rising -local n=DayOfYear -Tlocal=Tlocal or 0 -local rad=math.rad -local deg=math.deg -local floor=math.floor -local frac=function(n)return n-floor(n)end -local cos=function(d)return math.cos(rad(d))end -local acos=function(d)return deg(math.acos(d))end -local sin=function(d)return math.sin(rad(d))end -local asin=function(d)return deg(math.asin(d))end -local tan=function(d)return math.tan(rad(d))end -local atan=function(d)return deg(math.atan(d))end -local function fit_into_range(val,min,max) -local range=max-min -local count -if val=max then -count=floor((val-max)/range)+1 -return val-count*range -else -return val -end -end -local lng_hour=longitude/15 -local t -if rising then -t=n+((6-lng_hour)/24) -else -t=n+((18-lng_hour)/24) -end -local M=(0.9856*t)-3.289 -local L=fit_into_range(M+(1.916*sin(M))+(0.020*sin(2*M))+282.634,0,360) -local RA=fit_into_range(atan(0.91764*tan(L)),0,360) -local Lquadrant=floor(L/90)*90 -local RAquadrant=floor(RA/90)*90 -RA=RA+Lquadrant-RAquadrant -RA=RA/15 -local sinDec=0.39782*sin(L) -local cosDec=cos(asin(sinDec)) -local cosH=(cos(zenith)-(sinDec*sin(latitude)))/(cosDec*cos(latitude)) -if rising and cosH>1 then -return"N/R" -elseif cosH<-1 then -return"N/S" -end -local H -if rising then -H=360-acos(cosH) -else -H=acos(cosH) -end -H=H/15 -local T=H+RA-(0.06571*t)-6.622 -local UT=fit_into_range(T-lng_hour+Tlocal,0,24) -return floor(UT)*60*60+frac(UT)*60*60 -end -function UTILS.GetSunrise(Day,Month,Year,Latitude,Longitude,Tlocal) -local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day) -return UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tlocal) -end -function UTILS.GetSunset(Day,Month,Year,Latitude,Longitude,Tlocal) -local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day) -return UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tlocal) -end -function UTILS.GetOSTime() -if os then -return os.clock() -end -return nil -end -function UTILS.ShuffleTable(t) -if t==nil or type(t)~="table"then -BASE:I("Error in ShuffleTable: Missing or wrong tyåe of Argument") -return -end -math.random() -math.random() -math.random() -local TempTable={} -for i=1,#t do -local r=math.random(1,#t) -TempTable[i]=t[r] -table.remove(t,r) -end -return TempTable -end -PROFILER={ -ClassName="PROFILER", -Counters={}, -dInfo={}, -fTime={}, -fTimeTotal={}, -eventHandler={}, -logUnknown=false, -ThreshCPS=0.0, -ThreshTtot=0.005, -fileNamePrefix="MooseProfiler", -fileNameSuffix="txt" -} -function PROFILER.Start(Delay,Duration) -local go=true -if not os then -env.error("ERROR: Profiler needs os to be desanitized!") -go=false -end -if not io then -env.error("ERROR: Profiler needs io to be desanitized!") -go=false -end -if not lfs then -env.error("ERROR: Profiler needs lfs to be desanitized!") -go=false -end -if not go then -return -end -if Delay and Delay>0 then -BASE:ScheduleOnce(Delay,PROFILER.Start,0,Duration) -else -PROFILER.TstartGame=timer.getTime() -PROFILER.TstartOS=os.clock() -world.addEventHandler(PROFILER.eventHandler) -env.info('############################ Profiler Started ############################') -if Duration then -env.info(string.format("- Will be running for %d seconds",Duration)) -else -env.info(string.format("- Will be stopped when mission ends")) -end -env.info(string.format("- Calls per second threshold %.3f/sec",PROFILER.ThreshCPS)) -env.info(string.format("- Total function time threshold %.3f sec",PROFILER.ThreshTtot)) -env.info(string.format("- Output file \"%s\" in your DCS log file folder",PROFILER.getfilename(PROFILER.fileNameSuffix))) -env.info(string.format("- Output file \"%s\" in CSV format",PROFILER.getfilename("csv"))) -env.info('###############################################################################') -local duration=Duration or 600 -trigger.action.outText("### Profiler running ###",duration) -debug.sethook(PROFILER.hook,"cr") -if Duration then -PROFILER.Stop(Duration) -end -end -end -function PROFILER.Stop(Delay) -if Delay and Delay>0 then -BASE:ScheduleOnce(Delay,PROFILER.Stop) -else -debug.sethook() -local runTimeGame=timer.getTime()-PROFILER.TstartGame -local runTimeOS=os.clock()-PROFILER.TstartOS -PROFILER.showInfo(runTimeGame,runTimeOS) -end -end -function PROFILER.eventHandler:onEvent(event) -if event.id==world.event.S_EVENT_MISSION_END then -PROFILER.Stop() -end -end -function PROFILER.hook(event) -local f=debug.getinfo(2,"f").func -if event=='call'then -if PROFILER.Counters[f]==nil then -PROFILER.Counters[f]=1 -PROFILER.dInfo[f]=debug.getinfo(2,"Sn") -if PROFILER.fTimeTotal[f]==nil then -PROFILER.fTimeTotal[f]=0 -end -else -PROFILER.Counters[f]=PROFILER.Counters[f]+1 -end -if PROFILER.fTime[f]==nil then -PROFILER.fTime[f]=os.clock() -end -elseif(event=='return')then -if PROFILER.fTime[f]~=nil then -PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f]) -PROFILER.fTime[f]=nil -end -end -end -function PROFILER.getData(func) -local n=PROFILER.dInfo[func] -if n.what=="C"then -return n.name,"?","?",PROFILER.fTimeTotal[func] -end -return n.name,n.short_src,n.linedefined,PROFILER.fTimeTotal[func] -end -function PROFILER._flog(f,txt) -f:write(txt.."\r\n") -end -function PROFILER.showTable(data,f,runTimeGame) -for i=1,#data do -local t=data[i] -local cps=t.count/runTimeGame -local threshCPS=cps>=PROFILER.ThreshCPS -local threshTot=t.tm>=PROFILER.ThreshTtot -if threshCPS and threshTot then -local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s",t.func,t.count,cps,t.tm,t.tm/runTimeGame*100,t.tm/t.count,tostring(t.src),tostring(t.line)) -PROFILER._flog(f,text) -end -end -end -function PROFILER.printCSV(data,runTimeGame) -local file=PROFILER.getfilename("csv") -local g=io.open(file,'w') -local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number," -g:write(text.."\r\n") -for i=1,#data do -local t=data[i] -local cps=t.count/runTimeGame -local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,",t.func,t.count,cps,t.tm,t.tm/runTimeGame*100,t.tm/t.count,tostring(t.src),tostring(t.line)) -g:write(txt.."\r\n") -end -g:close() -end -function PROFILER.getfilename(ext) -local dir=lfs.writedir()..[[Logs\]] -ext=ext or PROFILER.fileNameSuffix -local file=dir..PROFILER.fileNamePrefix.."."..ext -if not UTILS.FileExists(file)then -return file -end -for i=1,999 do -local file=string.format("%s%s-%03d.%s",dir,PROFILER.fileNamePrefix,i,ext) -if not UTILS.FileExists(file)then -return file -end -end -end -function PROFILER.showInfo(runTimeGame,runTimeOS) -local file=PROFILER.getfilename(PROFILER.fileNameSuffix) -local f=io.open(file,'w') -local Ttot=0 -local Calls=0 -local t={} -local tcopy=nil -local tserialize=nil -local tforgen=nil -local tpairs=nil -for func,count in pairs(PROFILER.Counters)do -local s,src,line,tm=PROFILER.getData(func) -if PROFILER.logUnknown==true then -if s==nil then s=""end -end -if s~=nil then -local T= -{func=s, -src=src, -line=line, -count=count, -tm=tm, -} -if s=="_copy"then -if tcopy==nil then -tcopy=T -else -tcopy.count=tcopy.count+T.count -tcopy.tm=tcopy.tm+T.tm -end -elseif s=="_Serialize"then -if tserialize==nil then -tserialize=T -else -tserialize.count=tserialize.count+T.count -tserialize.tm=tserialize.tm+T.tm -end -elseif s=="(for generator)"then -if tforgen==nil then -tforgen=T -else -tforgen.count=tforgen.count+T.count -tforgen.tm=tforgen.tm+T.tm -end -elseif s=="pairs"then -if tpairs==nil then -tpairs=T -else -tpairs.count=tpairs.count+T.count -tpairs.tm=tpairs.tm+T.tm -end -else -table.insert(t,T) -end -Ttot=Ttot+tm -Calls=Calls+count -end -end -if tcopy then -table.insert(t,tcopy) -end -if tserialize then -table.insert(t,tserialize) -end -if tforgen then -table.insert(t,tforgen) -end -if tpairs then -table.insert(t,tpairs) -end -env.info('############################ Profiler Stopped ############################') -env.info(string.format("* Runtime Game : %s = %d sec",UTILS.SecondsToClock(runTimeGame,true),runTimeGame)) -env.info(string.format("* Runtime Real : %s = %d sec",UTILS.SecondsToClock(runTimeOS,true),runTimeOS)) -env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)",UTILS.SecondsToClock(Ttot,true),Ttot,Ttot/runTimeGame*100)) -env.info(string.format("* Total functions : %d",#t)) -env.info(string.format("* Total func calls : %d",Calls)) -env.info(string.format("* Writing to file : \"%s\"",file)) -env.info(string.format("* Writing to file : \"%s\"",PROFILER.getfilename("csv"))) -env.info("##############################################################################") -table.sort(t,function(a,b)return a.tm>b.tm end) -PROFILER._flog(f,"") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"") -PROFILER._flog(f,"-------------------------") -PROFILER._flog(f,"---- Profiler Report ----") -PROFILER._flog(f,"-------------------------") -PROFILER._flog(f,"") -PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec",UTILS.SecondsToClock(runTimeGame,true),runTimeGame)) -PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec",UTILS.SecondsToClock(runTimeOS,true),runTimeOS)) -PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)",UTILS.SecondsToClock(Ttot,true),Ttot,Ttot/runTimeGame*100)) -PROFILER._flog(f,"") -PROFILER._flog(f,string.format("* Total functions = %d",#t)) -PROFILER._flog(f,string.format("* Total func calls = %d",Calls)) -PROFILER._flog(f,"") -PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec",PROFILER.ThreshCPS)) -PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec",PROFILER.ThreshTtot)) -PROFILER._flog(f,"") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"") -PROFILER.showTable(t,f,runTimeGame) -table.sort(t,function(a,b)return a.tm/a.count>b.tm/b.count end) -PROFILER._flog(f,"") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"") -PROFILER._flog(f,"--------------------------------------") -PROFILER._flog(f,"---- Data Sorted by Time per Call ----") -PROFILER._flog(f,"--------------------------------------") -PROFILER._flog(f,"") -PROFILER.showTable(t,f,runTimeGame) -table.sort(t,function(a,b)return a.count>b.count end) -PROFILER._flog(f,"") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"") -PROFILER._flog(f,"------------------------------------") -PROFILER._flog(f,"---- Data Sorted by Total Calls ----") -PROFILER._flog(f,"------------------------------------") -PROFILER._flog(f,"") -PROFILER.showTable(t,f,runTimeGame) -PROFILER._flog(f,"") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"************************************************************************************************************************") -PROFILER._flog(f,"************************************************************************************************************************") -f:close() -PROFILER.printCSV(t,runTimeGame) -end -local _TraceOnOff=true -local _TraceLevel=1 -local _TraceAll=false -local _TraceClass={} -local _TraceClassMethod={} -local _ClassID=0 -BASE={ -ClassName="BASE", -ClassID=0, -Events={}, -States={}, -Debug=debug, -Scheduler=nil, -} -BASE.__={} -BASE._={ -Schedules={} -} -FORMATION={ -Cone="Cone", -Vee="Vee" -} -function BASE:New() -local self=routines.utils.deepCopy(self) -_ClassID=_ClassID+1 -self.ClassID=_ClassID -return self -end -function BASE:Inherit(Child,Parent) -local Child=routines.utils.deepCopy(Child) -if Child~=nil then -if rawget(Child,"__")then -setmetatable(Child,{__index=Child.__}) -setmetatable(Child.__,{__index=Parent}) -else -setmetatable(Child,{__index=Parent}) -end -end -return Child -end -local function getParent(Child) -local Parent=nil -if Child.ClassName=='BASE'then -Parent=nil -else -if rawget(Child,"__")then -Parent=getmetatable(Child.__).__index -else -Parent=getmetatable(Child).__index -end -end -return Parent -end -function BASE:GetParent(Child,FromClass) -local Parent -if Child.ClassName=='BASE'then -Parent=nil -else -if FromClass then -while(Child.ClassName~="BASE"and Child.ClassName~=FromClass.ClassName)do -Child=getParent(Child) -end -end -if Child.ClassName=='BASE'then -Parent=nil -else -Parent=getParent(Child) -end -end -return Parent -end -function BASE:IsInstanceOf(ClassName) -if type(ClassName)~='string'then -if type(ClassName)=='table'and ClassName.ClassName~=nil then -ClassName=ClassName.ClassName -else -local err_str='className parameter should be a string; parameter received: '..type(ClassName) -self:E(err_str) -return false -end -end -ClassName=string.upper(ClassName) -if string.upper(self.ClassName)==ClassName then -return true -end -local Parent=getParent(self) -while Parent do -if string.upper(Parent.ClassName)==ClassName then -return true -end -Parent=getParent(Parent) -end -return false -end -function BASE:GetClassNameAndID() -return string.format('%s#%09d',self.ClassName,self.ClassID) -end -function BASE:GetClassName() -return self.ClassName -end -function BASE:GetClassID() -return self.ClassID -end -do -function BASE:EventDispatcher() -return _EVENTDISPATCHER -end -function BASE:GetEventPriority() -return self._.EventPriority or 5 -end -function BASE:SetEventPriority(EventPriority) -self._.EventPriority=EventPriority -end -function BASE:EventRemoveAll() -self:EventDispatcher():RemoveAll(self) -return self -end -function BASE:HandleEvent(EventID,EventFunction) -self:EventDispatcher():OnEventGeneric(EventFunction,self,EventID) -return self -end -function BASE:UnHandleEvent(EventID) -self:EventDispatcher():RemoveEvent(self,EventID) -return self -end -end -function BASE:CreateEventBirth(EventTime,Initiator,IniUnitName,place,subplace) -self:F({EventTime,Initiator,IniUnitName,place,subplace}) -local Event={ -id=world.event.S_EVENT_BIRTH, -time=EventTime, -initiator=Initiator, -IniUnitName=IniUnitName, -place=place, -subplace=subplace -} -world.onEvent(Event) -end -function BASE:CreateEventCrash(EventTime,Initiator) -self:F({EventTime,Initiator}) -local Event={ -id=world.event.S_EVENT_CRASH, -time=EventTime, -initiator=Initiator, -} -world.onEvent(Event) -end -function BASE:CreateEventUnitLost(EventTime,Initiator) -self:F({EventTime,Initiator}) -local Event={ -id=world.event.S_EVENT_UNIT_LOST, -time=EventTime, -initiator=Initiator, -} -world.onEvent(Event) -end -function BASE:CreateEventDead(EventTime,Initiator) -self:F({EventTime,Initiator}) -local Event={ -id=world.event.S_EVENT_DEAD, -time=EventTime, -initiator=Initiator, -} -world.onEvent(Event) -end -function BASE:CreateEventRemoveUnit(EventTime,Initiator) -self:F({EventTime,Initiator}) -local Event={ -id=EVENTS.RemoveUnit, -time=EventTime, -initiator=Initiator, -} -world.onEvent(Event) -end -function BASE:CreateEventTakeoff(EventTime,Initiator) -self:F({EventTime,Initiator}) -local Event={ -id=world.event.S_EVENT_TAKEOFF, -time=EventTime, -initiator=Initiator, -} -world.onEvent(Event) -end -function BASE:CreateEventPlayerEnterAircraft(PlayerUnit) -self:F({PlayerUnit}) -local Event={ -id=EVENTS.PlayerEnterAircraft, -time=timer.getTime(), -initiator=PlayerUnit:GetDCSObject() -} -world.onEvent(Event) -end -function BASE:onEvent(event) -if self then -for EventID,EventObject in pairs(self.Events)do -if EventObject.EventEnabled then -if event.id==EventObject.Event then -if self==EventObject.Self then -if event.initiator and event.initiator:isExist()then -event.IniUnitName=event.initiator:getName() -end -if event.target and event.target:isExist()then -event.TgtUnitName=event.target:getName() -end -end -end -end -end -end -end -do -function BASE:ScheduleOnce(Start,SchedulerFunction,...) -self:F2({Start}) -self:T3({...}) -local ObjectName="-" -ObjectName=self.ClassName..self.ClassID -self:F3({"ScheduleOnce: ",ObjectName,Start}) -if not self.Scheduler then -self.Scheduler=SCHEDULER:New(self) -end -local ScheduleID=_SCHEDULEDISPATCHER:AddSchedule( -self, -SchedulerFunction, -{...}, -Start, -nil, -nil, -nil -) -self._.Schedules[#self._.Schedules+1]=ScheduleID -return self._.Schedules[#self._.Schedules] -end -function BASE:ScheduleRepeat(Start,Repeat,RandomizeFactor,Stop,SchedulerFunction,...) -self:F2({Start}) -self:T3({...}) -local ObjectName="-" -ObjectName=self.ClassName..self.ClassID -self:F3({"ScheduleRepeat: ",ObjectName,Start,Repeat,RandomizeFactor,Stop}) -if not self.Scheduler then -self.Scheduler=SCHEDULER:New(self) -end -local ScheduleID=self.Scheduler:Schedule( -self, -SchedulerFunction, -{...}, -Start, -Repeat, -RandomizeFactor, -Stop, -4 -) -self._.Schedules[#self._.Schedules+1]=ScheduleID -return self._.Schedules[#self._.Schedules] -end -function BASE:ScheduleStop(SchedulerFunction) -self:F3({"ScheduleStop:"}) -if self.Scheduler then -_SCHEDULEDISPATCHER:Stop(self.Scheduler,self._.Schedules[SchedulerFunction]) -end -end -end -function BASE:SetState(Object,Key,Value) -local ClassNameAndID=Object:GetClassNameAndID() -self.States[ClassNameAndID]=self.States[ClassNameAndID]or{} -self.States[ClassNameAndID][Key]=Value -return self.States[ClassNameAndID][Key] -end -function BASE:GetState(Object,Key) -local ClassNameAndID=Object:GetClassNameAndID() -if self.States[ClassNameAndID]then -local Value=self.States[ClassNameAndID][Key]or false -return Value -end -return nil -end -function BASE:ClearState(Object,StateName) -local ClassNameAndID=Object:GetClassNameAndID() -if self.States[ClassNameAndID]then -self.States[ClassNameAndID][StateName]=nil -end -end -function BASE:TraceOn() -self:TraceOnOff(true) -end -function BASE:TraceOff() -self:TraceOnOff(false) -end -function BASE:TraceOnOff(TraceOnOff) -if TraceOnOff==false then -self:I("Tracing in MOOSE is OFF") -_TraceOnOff=false -else -self:I("Tracing in MOOSE is ON") -_TraceOnOff=true -end -end -function BASE:IsTrace() -if BASE.Debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then -return true -else -return false -end -end -function BASE:TraceLevel(Level) -_TraceLevel=Level or 1 -self:I("Tracing level ".._TraceLevel) -end -function BASE:TraceAll(TraceAll) -if TraceAll==false then -_TraceAll=false -else -_TraceAll=true -end -if _TraceAll then -self:I("Tracing all methods in MOOSE ") -else -self:I("Switched off tracing all methods in MOOSE") -end -end -function BASE:TraceClass(Class) -_TraceClass[Class]=true -_TraceClassMethod[Class]={} -self:I("Tracing class "..Class) -end -function BASE:TraceClassMethod(Class,Method) -if not _TraceClassMethod[Class]then -_TraceClassMethod[Class]={} -_TraceClassMethod[Class].Method={} -end -_TraceClassMethod[Class].Method[Method]=true -self:I("Tracing method "..Method.." of class "..Class) -end -function BASE:_F(Arguments,DebugInfoCurrentParam,DebugInfoFromParam) -if BASE.Debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then -local DebugInfoCurrent=DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo(3,"l") -local Function="function" -if DebugInfoCurrent.name then -Function=DebugInfoCurrent.name -end -if _TraceAll==true or _TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName].Method[Function]then -local LineCurrent=0 -if DebugInfoCurrent.currentline then -LineCurrent=DebugInfoCurrent.currentline -end -local LineFrom=0 -if DebugInfoFrom then -LineFrom=DebugInfoFrom.currentline -end -env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s(%s)",LineCurrent,LineFrom,"F",self.ClassName,self.ClassID,Function,routines.utils.oneLineSerialize(Arguments))) -end -end -end -function BASE:F(Arguments) -if BASE.Debug and _TraceOnOff then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -if _TraceLevel>=1 then -self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom) -end -end -end -function BASE:F2(Arguments) -if BASE.Debug and _TraceOnOff then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -if _TraceLevel>=2 then -self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom) -end -end -end -function BASE:F3(Arguments) -if BASE.Debug and _TraceOnOff then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -if _TraceLevel>=3 then -self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom) -end -end -end -function BASE:_T(Arguments,DebugInfoCurrentParam,DebugInfoFromParam) -if BASE.Debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then -local DebugInfoCurrent=DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo(3,"l") -local Function="function" -if DebugInfoCurrent.name then -Function=DebugInfoCurrent.name -end -if _TraceAll==true or _TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName].Method[Function]then -local LineCurrent=0 -if DebugInfoCurrent.currentline then -LineCurrent=DebugInfoCurrent.currentline -end -local LineFrom=0 -if DebugInfoFrom then -LineFrom=DebugInfoFrom.currentline -end -env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s",LineCurrent,LineFrom,"T",self.ClassName,self.ClassID,routines.utils.oneLineSerialize(Arguments))) -end -end -end -function BASE:T(Arguments) -if BASE.Debug and _TraceOnOff then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -if _TraceLevel>=1 then -self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom) -end -end -end -function BASE:T2(Arguments) -if BASE.Debug and _TraceOnOff then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -if _TraceLevel>=2 then -self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom) -end -end -end -function BASE:T3(Arguments) -if BASE.Debug and _TraceOnOff then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -if _TraceLevel>=3 then -self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom) -end -end -end -function BASE:E(Arguments) -if BASE.Debug then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -local Function="function" -if DebugInfoCurrent.name then -Function=DebugInfoCurrent.name -end -local LineCurrent=DebugInfoCurrent.currentline -local LineFrom=-1 -if DebugInfoFrom then -LineFrom=DebugInfoFrom.currentline -end -env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s(%s)",LineCurrent,LineFrom,"E",self.ClassName,self.ClassID,Function,routines.utils.oneLineSerialize(Arguments))) -else -env.info(string.format("%1s:%30s%05d(%s)","E",self.ClassName,self.ClassID,routines.utils.oneLineSerialize(Arguments))) -end -end -function BASE:I(Arguments) -if BASE.Debug then -local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl") -local DebugInfoFrom=BASE.Debug.getinfo(3,"l") -local Function="function" -if DebugInfoCurrent.name then -Function=DebugInfoCurrent.name -end -local LineCurrent=DebugInfoCurrent.currentline -local LineFrom=-1 -if DebugInfoFrom then -LineFrom=DebugInfoFrom.currentline -end -env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s(%s)",LineCurrent,LineFrom,"I",self.ClassName,self.ClassID,Function,routines.utils.oneLineSerialize(Arguments))) -else -env.info(string.format("%1s:%30s%05d(%s)","I",self.ClassName,self.ClassID,routines.utils.oneLineSerialize(Arguments))) -end -end -do -USERFLAG={ -ClassName="USERFLAG", -UserFlagName=nil, -} -function USERFLAG:New(UserFlagName) -local self=BASE:Inherit(self,BASE:New()) -self.UserFlagName=UserFlagName -return self -end -function USERFLAG:GetName() -return self.UserFlagName -end -function USERFLAG:Set(Number,Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,USERFLAG.Set,self,Number) -else -trigger.action.setUserFlag(self.UserFlagName,Number) -end -return self -end -function USERFLAG:Get() -return trigger.misc.getUserFlag(self.UserFlagName) -end -function USERFLAG:Is(Number) -return trigger.misc.getUserFlag(self.UserFlagName)==Number -end -end -do -USERSOUND={ -ClassName="USERSOUND", -} -function USERSOUND:New(UserSoundFileName) -local self=BASE:Inherit(self,BASE:New()) -self.UserSoundFileName=UserSoundFileName -return self -end -function USERSOUND:SetFileName(UserSoundFileName) -self.UserSoundFileName=UserSoundFileName -return self -end -function USERSOUND:ToAll() -trigger.action.outSound(self.UserSoundFileName) -return self -end -function USERSOUND:ToCoalition(Coalition) -trigger.action.outSoundForCoalition(Coalition,self.UserSoundFileName) -return self -end -function USERSOUND:ToCountry(Country) -trigger.action.outSoundForCountry(Country,self.UserSoundFileName) -return self -end -function USERSOUND:ToGroup(Group,Delay) -Delay=Delay or 0 -if Delay>0 then -SCHEDULER:New(nil,USERSOUND.ToGroup,{self,Group},Delay) -else -trigger.action.outSoundForGroup(Group:GetID(),self.UserSoundFileName) -end -return self -end -end -REPORT={ -ClassName="REPORT", -Title="", -} -function REPORT:New(Title) -local self=BASE:Inherit(self,BASE:New()) -self.Report={} -self:SetTitle(Title or"") -self:SetIndent(3) -return self -end -function REPORT:HasText() -return#self.Report>0 -end -function REPORT:SetIndent(Indent) -self.Indent=Indent -return self -end -function REPORT:Add(Text) -self.Report[#self.Report+1]=Text -return self -end -function REPORT:AddIndent(Text,Separator) -self.Report[#self.Report+1]=((Separator and Separator..string.rep(" ",self.Indent-1))or string.rep(" ",self.Indent))..Text:gsub("\n","\n"..string.rep(" ",self.Indent)) -return self -end -function REPORT:Text(Delimiter) -Delimiter=Delimiter or"\n" -local ReportText=(self.Title~=""and self.Title..Delimiter or self.Title)..table.concat(self.Report,Delimiter)or"" -return ReportText -end -function REPORT:SetTitle(Title) -self.Title=Title -return self -end -function REPORT:GetCount() -return#self.Report -end -SCHEDULER={ -ClassName="SCHEDULER", -Schedules={}, -MasterObject=nil, -ShowTrace=nil, -} -function SCHEDULER:New(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop) -local self=BASE:Inherit(self,BASE:New()) -self:F2({Start,Repeat,RandomizeFactor,Stop}) -local ScheduleID=nil -self.MasterObject=MasterObject -self.ShowTrace=false -if SchedulerFunction then -ScheduleID=self:Schedule(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop,3) -end -return self,ScheduleID -end -function SCHEDULER:Schedule(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop,TraceLevel,Fsm) -self:F2({Start,Repeat,RandomizeFactor,Stop}) -self:T3({SchedulerArguments}) -local ObjectName="-" -if MasterObject and MasterObject.ClassName and MasterObject.ClassID then -ObjectName=MasterObject.ClassName..MasterObject.ClassID -end -self:F3({"Schedule :",ObjectName,tostring(MasterObject),Start,Repeat,RandomizeFactor,Stop}) -self.MasterObject=MasterObject -local ScheduleID=_SCHEDULEDISPATCHER:AddSchedule( -self, -SchedulerFunction, -SchedulerArguments, -Start, -Repeat, -RandomizeFactor, -Stop, -TraceLevel or 3, -Fsm -) -self.Schedules[#self.Schedules+1]=ScheduleID -return ScheduleID -end -function SCHEDULER:Start(ScheduleID) -self:F3({ScheduleID}) -self:T(string.format("Starting scheduler ID=%s",tostring(ScheduleID))) -_SCHEDULEDISPATCHER:Start(self,ScheduleID) -end -function SCHEDULER:Stop(ScheduleID) -self:F3({ScheduleID}) -self:T(string.format("Stopping scheduler ID=%s",tostring(ScheduleID))) -_SCHEDULEDISPATCHER:Stop(self,ScheduleID) -end -function SCHEDULER:Remove(ScheduleID) -self:F3({ScheduleID}) -self:T(string.format("Removing scheduler ID=%s",tostring(ScheduleID))) -_SCHEDULEDISPATCHER:RemoveSchedule(self,ScheduleID) -end -function SCHEDULER:Clear() -self:F3() -self:T(string.format("Clearing scheduler")) -_SCHEDULEDISPATCHER:Clear(self) -end -function SCHEDULER:ShowTrace() -_SCHEDULEDISPATCHER:ShowTrace(self) -end -function SCHEDULER:NoTrace() -_SCHEDULEDISPATCHER:NoTrace(self) -end -SCHEDULEDISPATCHER={ -ClassName="SCHEDULEDISPATCHER", -CallID=0, -PersistentSchedulers={}, -ObjectSchedulers={}, -Schedule=nil, -} -function SCHEDULEDISPATCHER:New() -local self=BASE:Inherit(self,BASE:New()) -self:F3() -return self -end -function SCHEDULEDISPATCHER:AddSchedule(Scheduler,ScheduleFunction,ScheduleArguments,Start,Repeat,Randomize,Stop,TraceLevel,Fsm) -self:F2({Scheduler,ScheduleFunction,ScheduleArguments,Start,Repeat,Randomize,Stop,TraceLevel,Fsm}) -self.CallID=self.CallID+1 -local CallID=self.CallID.."#"..(Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID()or"")or"" -self:T2(string.format("Adding schedule #%d CallID=%s",self.CallID,CallID)) -self.PersistentSchedulers=self.PersistentSchedulers or{} -self.ObjectSchedulers=self.ObjectSchedulers or setmetatable({},{__mode="v"}) -if Scheduler.MasterObject then -self.ObjectSchedulers[CallID]=Scheduler -self:F3({CallID=CallID,ObjectScheduler=tostring(self.ObjectSchedulers[CallID]),MasterObject=tostring(Scheduler.MasterObject)}) -else -self.PersistentSchedulers[CallID]=Scheduler -self:F3({CallID=CallID,PersistentScheduler=self.PersistentSchedulers[CallID]}) -end -self.Schedule=self.Schedule or setmetatable({},{__mode="k"}) -self.Schedule[Scheduler]=self.Schedule[Scheduler]or{} -self.Schedule[Scheduler][CallID]={} -self.Schedule[Scheduler][CallID].Function=ScheduleFunction -self.Schedule[Scheduler][CallID].Arguments=ScheduleArguments -self.Schedule[Scheduler][CallID].StartTime=timer.getTime()+(Start or 0) -self.Schedule[Scheduler][CallID].Start=Start+0.001 -self.Schedule[Scheduler][CallID].Repeat=Repeat or 0 -self.Schedule[Scheduler][CallID].Randomize=Randomize or 0 -self.Schedule[Scheduler][CallID].Stop=Stop -local Info={} -if debug then -TraceLevel=TraceLevel or 2 -Info=debug.getinfo(TraceLevel,"nlS") -local name_fsm=debug.getinfo(TraceLevel-1,"n").name -if name_fsm then -Info.name=name_fsm -end -end -self:T3(self.Schedule[Scheduler][CallID]) -self.Schedule[Scheduler][CallID].CallHandler=function(Params) -local CallID=Params.CallID -local Info=Params.Info or{} -local Source=Info.source or"?" -local Line=Info.currentline or"?" -local Name=Info.name or"?" -local ErrorHandler=function(errmsg) -env.info("Error in timer function: "..errmsg) -if BASE.Debug~=nil then -env.info(BASE.Debug.traceback()) -end -return errmsg -end -local Scheduler=self.ObjectSchedulers[CallID] -if not Scheduler then -Scheduler=self.PersistentSchedulers[CallID] -end -if Scheduler then -local MasterObject=tostring(Scheduler.MasterObject) -local Schedule=self.Schedule[Scheduler][CallID] -local SchedulerObject=Scheduler.MasterObject -local ShowTrace=Scheduler.ShowTrace -local ScheduleFunction=Schedule.Function -local ScheduleArguments=Schedule.Arguments or{} -local Start=Schedule.Start -local Repeat=Schedule.Repeat or 0 -local Randomize=Schedule.Randomize or 0 -local Stop=Schedule.Stop or 0 -local ScheduleID=Schedule.ScheduleID -local Prefix=(Repeat==0)and"--->"or"+++>" -local Status,Result -if SchedulerObject then -local function Timer() -if ShowTrace then -SchedulerObject:T(Prefix..Name..":"..Line.." ("..Source..")") -end -return ScheduleFunction(SchedulerObject,unpack(ScheduleArguments)) -end -Status,Result=xpcall(Timer,ErrorHandler) -else -local function Timer() -if ShowTrace then -self:T(Prefix..Name..":"..Line.." ("..Source..")") -end -return ScheduleFunction(unpack(ScheduleArguments)) -end -Status,Result=xpcall(Timer,ErrorHandler) -end -local CurrentTime=timer.getTime() -local StartTime=Schedule.StartTime -self:F3({CallID=CallID,ScheduleID=ScheduleID,Master=MasterObject,CurrentTime=CurrentTime,StartTime=StartTime,Start=Start,Repeat=Repeat,Randomize=Randomize,Stop=Stop}) -if Status and((Result==nil)or(Result and Result~=false))then -if Repeat~=0 and((Stop==0)or(Stop~=0 and CurrentTime<=StartTime+Stop))then -local ScheduleTime=CurrentTime+Repeat+math.random(-(Randomize*Repeat/2),(Randomize*Repeat/2))+0.0001 -return ScheduleTime -else -self:Stop(Scheduler,CallID) -end -else -self:Stop(Scheduler,CallID) -end -else -self:I("<<<>"..Name..":"..Line.." ("..Source..")") -end -return nil -end -self:Start(Scheduler,CallID,Info) -return CallID -end -function SCHEDULEDISPATCHER:RemoveSchedule(Scheduler,CallID) -self:F2({Remove=CallID,Scheduler=Scheduler}) -if CallID then -self:Stop(Scheduler,CallID) -self.Schedule[Scheduler][CallID]=nil -end -end -function SCHEDULEDISPATCHER:Start(Scheduler,CallID,Info) -self:F2({Start=CallID,Scheduler=Scheduler}) -if CallID then -local Schedule=self.Schedule[Scheduler][CallID] -if not Schedule.ScheduleID then -local Tnow=timer.getTime() -Schedule.StartTime=Tnow -Schedule.ScheduleID=timer.scheduleFunction(Schedule.CallHandler,{CallID=CallID,Info=Info},Tnow+Schedule.Start) -self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s",tostring(CallID),tostring(Schedule.ScheduleID))) -end -else -for CallID,Schedule in pairs(self.Schedule[Scheduler]or{})do -self:Start(Scheduler,CallID,Info) -end -end -end -function SCHEDULEDISPATCHER:Stop(Scheduler,CallID) -self:F2({Stop=CallID,Scheduler=Scheduler}) -if CallID then -local Schedule=self.Schedule[Scheduler][CallID] -if Schedule.ScheduleID then -self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s",tostring(CallID),tostring(Schedule.ScheduleID))) -timer.removeFunction(Schedule.ScheduleID) -Schedule.ScheduleID=nil -else -self:T(string.format("Error no ScheduleID for CallID=%s",tostring(CallID))) -end -else -for CallID,Schedule in pairs(self.Schedule[Scheduler]or{})do -self:Stop(Scheduler,CallID) -end -end -end -function SCHEDULEDISPATCHER:Clear(Scheduler) -self:F2({Scheduler=Scheduler}) -for CallID,Schedule in pairs(self.Schedule[Scheduler]or{})do -self:Stop(Scheduler,CallID) -end -end -function SCHEDULEDISPATCHER:ShowTrace(Scheduler) -self:F2({Scheduler=Scheduler}) -Scheduler.ShowTrace=true -end -function SCHEDULEDISPATCHER:NoTrace(Scheduler) -self:F2({Scheduler=Scheduler}) -Scheduler.ShowTrace=false -end -EVENT={ -ClassName="EVENT", -ClassID=0, -MissionEnd=false, -} -world.event.S_EVENT_NEW_CARGO=world.event.S_EVENT_MAX+1000 -world.event.S_EVENT_DELETE_CARGO=world.event.S_EVENT_MAX+1001 -world.event.S_EVENT_NEW_ZONE=world.event.S_EVENT_MAX+1002 -world.event.S_EVENT_DELETE_ZONE=world.event.S_EVENT_MAX+1003 -world.event.S_EVENT_NEW_ZONE_GOAL=world.event.S_EVENT_MAX+1004 -world.event.S_EVENT_DELETE_ZONE_GOAL=world.event.S_EVENT_MAX+1005 -world.event.S_EVENT_REMOVE_UNIT=world.event.S_EVENT_MAX+1006 -world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT=world.event.S_EVENT_MAX+1007 -EVENTS={ -Shot=world.event.S_EVENT_SHOT, -Hit=world.event.S_EVENT_HIT, -Takeoff=world.event.S_EVENT_TAKEOFF, -Land=world.event.S_EVENT_LAND, -Crash=world.event.S_EVENT_CRASH, -Ejection=world.event.S_EVENT_EJECTION, -Refueling=world.event.S_EVENT_REFUELING, -Dead=world.event.S_EVENT_DEAD, -PilotDead=world.event.S_EVENT_PILOT_DEAD, -BaseCaptured=world.event.S_EVENT_BASE_CAPTURED, -MissionStart=world.event.S_EVENT_MISSION_START, -MissionEnd=world.event.S_EVENT_MISSION_END, -TookControl=world.event.S_EVENT_TOOK_CONTROL, -RefuelingStop=world.event.S_EVENT_REFUELING_STOP, -Birth=world.event.S_EVENT_BIRTH, -HumanFailure=world.event.S_EVENT_HUMAN_FAILURE, -EngineStartup=world.event.S_EVENT_ENGINE_STARTUP, -EngineShutdown=world.event.S_EVENT_ENGINE_SHUTDOWN, -PlayerEnterUnit=world.event.S_EVENT_PLAYER_ENTER_UNIT, -PlayerLeaveUnit=world.event.S_EVENT_PLAYER_LEAVE_UNIT, -PlayerComment=world.event.S_EVENT_PLAYER_COMMENT, -ShootingStart=world.event.S_EVENT_SHOOTING_START, -ShootingEnd=world.event.S_EVENT_SHOOTING_END, -MarkAdded=world.event.S_EVENT_MARK_ADDED, -MarkChange=world.event.S_EVENT_MARK_CHANGE, -MarkRemoved=world.event.S_EVENT_MARK_REMOVED, -NewCargo=world.event.S_EVENT_NEW_CARGO, -DeleteCargo=world.event.S_EVENT_DELETE_CARGO, -NewZone=world.event.S_EVENT_NEW_ZONE, -DeleteZone=world.event.S_EVENT_DELETE_ZONE, -NewZoneGoal=world.event.S_EVENT_NEW_ZONE_GOAL, -DeleteZoneGoal=world.event.S_EVENT_DELETE_ZONE_GOAL, -RemoveUnit=world.event.S_EVENT_REMOVE_UNIT, -PlayerEnterAircraft=world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT, -DetailedFailure=world.event.S_EVENT_DETAILED_FAILURE or-1, -Kill=world.event.S_EVENT_KILL or-1, -Score=world.event.S_EVENT_SCORE or-1, -UnitLost=world.event.S_EVENT_UNIT_LOST or-1, -LandingAfterEjection=world.event.S_EVENT_LANDING_AFTER_EJECTION or-1, -ParatrooperLanding=world.event.S_EVENT_PARATROOPER_LENDING or-1, -DiscardChairAfterEjection=world.event.S_EVENT_DISCARD_CHAIR_AFTER_EJECTION or-1, -WeaponAdd=world.event.S_EVENT_WEAPON_ADD or-1, -TriggerZone=world.event.S_EVENT_TRIGGER_ZONE or-1, -LandingQualityMark=world.event.S_EVENT_LANDING_QUALITY_MARK or-1, -BDA=world.event.S_EVENT_BDA or-1, -} -local _EVENTMETA={ -[world.event.S_EVENT_SHOT]={ -Order=1, -Side="I", -Event="OnEventShot", -Text="S_EVENT_SHOT" -}, -[world.event.S_EVENT_HIT]={ -Order=1, -Side="T", -Event="OnEventHit", -Text="S_EVENT_HIT" -}, -[world.event.S_EVENT_TAKEOFF]={ -Order=1, -Side="I", -Event="OnEventTakeoff", -Text="S_EVENT_TAKEOFF" -}, -[world.event.S_EVENT_LAND]={ -Order=1, -Side="I", -Event="OnEventLand", -Text="S_EVENT_LAND" -}, -[world.event.S_EVENT_CRASH]={ -Order=-1, -Side="I", -Event="OnEventCrash", -Text="S_EVENT_CRASH" -}, -[world.event.S_EVENT_EJECTION]={ -Order=1, -Side="I", -Event="OnEventEjection", -Text="S_EVENT_EJECTION" -}, -[world.event.S_EVENT_REFUELING]={ -Order=1, -Side="I", -Event="OnEventRefueling", -Text="S_EVENT_REFUELING" -}, -[world.event.S_EVENT_DEAD]={ -Order=-1, -Side="I", -Event="OnEventDead", -Text="S_EVENT_DEAD" -}, -[world.event.S_EVENT_PILOT_DEAD]={ -Order=1, -Side="I", -Event="OnEventPilotDead", -Text="S_EVENT_PILOT_DEAD" -}, -[world.event.S_EVENT_BASE_CAPTURED]={ -Order=1, -Side="I", -Event="OnEventBaseCaptured", -Text="S_EVENT_BASE_CAPTURED" -}, -[world.event.S_EVENT_MISSION_START]={ -Order=1, -Side="N", -Event="OnEventMissionStart", -Text="S_EVENT_MISSION_START" -}, -[world.event.S_EVENT_MISSION_END]={ -Order=1, -Side="N", -Event="OnEventMissionEnd", -Text="S_EVENT_MISSION_END" -}, -[world.event.S_EVENT_TOOK_CONTROL]={ -Order=1, -Side="N", -Event="OnEventTookControl", -Text="S_EVENT_TOOK_CONTROL" -}, -[world.event.S_EVENT_REFUELING_STOP]={ -Order=1, -Side="I", -Event="OnEventRefuelingStop", -Text="S_EVENT_REFUELING_STOP" -}, -[world.event.S_EVENT_BIRTH]={ -Order=1, -Side="I", -Event="OnEventBirth", -Text="S_EVENT_BIRTH" -}, -[world.event.S_EVENT_HUMAN_FAILURE]={ -Order=1, -Side="I", -Event="OnEventHumanFailure", -Text="S_EVENT_HUMAN_FAILURE" -}, -[world.event.S_EVENT_ENGINE_STARTUP]={ -Order=1, -Side="I", -Event="OnEventEngineStartup", -Text="S_EVENT_ENGINE_STARTUP" -}, -[world.event.S_EVENT_ENGINE_SHUTDOWN]={ -Order=1, -Side="I", -Event="OnEventEngineShutdown", -Text="S_EVENT_ENGINE_SHUTDOWN" -}, -[world.event.S_EVENT_PLAYER_ENTER_UNIT]={ -Order=1, -Side="I", -Event="OnEventPlayerEnterUnit", -Text="S_EVENT_PLAYER_ENTER_UNIT" -}, -[world.event.S_EVENT_PLAYER_LEAVE_UNIT]={ -Order=-1, -Side="I", -Event="OnEventPlayerLeaveUnit", -Text="S_EVENT_PLAYER_LEAVE_UNIT" -}, -[world.event.S_EVENT_PLAYER_COMMENT]={ -Order=1, -Side="I", -Event="OnEventPlayerComment", -Text="S_EVENT_PLAYER_COMMENT" -}, -[world.event.S_EVENT_SHOOTING_START]={ -Order=1, -Side="I", -Event="OnEventShootingStart", -Text="S_EVENT_SHOOTING_START" -}, -[world.event.S_EVENT_SHOOTING_END]={ -Order=1, -Side="I", -Event="OnEventShootingEnd", -Text="S_EVENT_SHOOTING_END" -}, -[world.event.S_EVENT_MARK_ADDED]={ -Order=1, -Side="I", -Event="OnEventMarkAdded", -Text="S_EVENT_MARK_ADDED" -}, -[world.event.S_EVENT_MARK_CHANGE]={ -Order=1, -Side="I", -Event="OnEventMarkChange", -Text="S_EVENT_MARK_CHANGE" -}, -[world.event.S_EVENT_MARK_REMOVED]={ -Order=1, -Side="I", -Event="OnEventMarkRemoved", -Text="S_EVENT_MARK_REMOVED" -}, -[EVENTS.NewCargo]={ -Order=1, -Event="OnEventNewCargo", -Text="S_EVENT_NEW_CARGO" -}, -[EVENTS.DeleteCargo]={ -Order=1, -Event="OnEventDeleteCargo", -Text="S_EVENT_DELETE_CARGO" -}, -[EVENTS.NewZone]={ -Order=1, -Event="OnEventNewZone", -Text="S_EVENT_NEW_ZONE" -}, -[EVENTS.DeleteZone]={ -Order=1, -Event="OnEventDeleteZone", -Text="S_EVENT_DELETE_ZONE" -}, -[EVENTS.NewZoneGoal]={ -Order=1, -Event="OnEventNewZoneGoal", -Text="S_EVENT_NEW_ZONE_GOAL" -}, -[EVENTS.DeleteZoneGoal]={ -Order=1, -Event="OnEventDeleteZoneGoal", -Text="S_EVENT_DELETE_ZONE_GOAL" -}, -[EVENTS.RemoveUnit]={ -Order=-1, -Event="OnEventRemoveUnit", -Text="S_EVENT_REMOVE_UNIT" -}, -[EVENTS.PlayerEnterAircraft]={ -Order=1, -Event="OnEventPlayerEnterAircraft", -Text="S_EVENT_PLAYER_ENTER_AIRCRAFT" -}, -[EVENTS.DetailedFailure]={ -Order=1, -Event="OnEventDetailedFailure", -Text="S_EVENT_DETAILED_FAILURE" -}, -[EVENTS.Kill]={ -Order=1, -Event="OnEventKill", -Text="S_EVENT_KILL" -}, -[EVENTS.Score]={ -Order=1, -Event="OnEventScore", -Text="S_EVENT_SCORE" -}, -[EVENTS.UnitLost]={ -Order=1, -Event="OnEventUnitLost", -Text="S_EVENT_UNIT_LOST" -}, -[EVENTS.LandingAfterEjection]={ -Order=1, -Event="OnEventLandingAfterEjection", -Text="S_EVENT_LANDING_AFTER_EJECTION" -}, -[EVENTS.ParatrooperLanding]={ -Order=1, -Event="OnEventParatrooperLanding", -Text="S_EVENT_PARATROOPER_LENDING" -}, -[EVENTS.DiscardChairAfterEjection]={ -Order=1, -Event="OnEventDiscardChairAfterEjection", -Text="S_EVENT_DISCARD_CHAIR_AFTER_EJECTION" -}, -[EVENTS.WeaponAdd]={ -Order=1, -Event="OnEventWeaponAdd", -Text="S_EVENT_WEAPON_ADD" -}, -[EVENTS.TriggerZone]={ -Order=1, -Event="OnEventTriggerZone", -Text="S_EVENT_TRIGGER_ZONE" -}, -[EVENTS.LandingQualityMark]={ -Order=1, -Event="OnEventLandingQualityMark", -Text="S_EVENT_LANDING_QUALITYMARK" -}, -[EVENTS.BDA]={ -Order=1, -Event="OnEventBDA", -Text="S_EVENT_BDA" -}, -} -function EVENT:New() -local self=BASE:Inherit(self,BASE:New()) -self.EventHandler=world.addEventHandler(self) -return self -end -function EVENT:Init(EventID,EventClass) -self:F3({_EVENTMETA[EventID].Text,EventClass}) -if not self.Events[EventID]then -self.Events[EventID]={} -end -local EventPriority=EventClass:GetEventPriority() -if not self.Events[EventID][EventPriority]then -self.Events[EventID][EventPriority]=setmetatable({},{__mode="k"}) -end -if not self.Events[EventID][EventPriority][EventClass]then -self.Events[EventID][EventPriority][EventClass]={} -end -return self.Events[EventID][EventPriority][EventClass] -end -function EVENT:RemoveEvent(EventClass,EventID) -self:F2({"Removing subscription for class: ",EventClass:GetClassNameAndID()}) -local EventPriority=EventClass:GetEventPriority() -self.Events=self.Events or{} -self.Events[EventID]=self.Events[EventID]or{} -self.Events[EventID][EventPriority]=self.Events[EventID][EventPriority]or{} -self.Events[EventID][EventPriority][EventClass]=nil -return self -end -function EVENT:Reset(EventObject) -self:F({"Resetting subscriptions for class: ",EventObject:GetClassNameAndID()}) -local EventPriority=EventObject:GetEventPriority() -for EventID,EventData in pairs(self.Events)do -if self.EventsDead then -if self.EventsDead[EventID]then -if self.EventsDead[EventID][EventPriority]then -if self.EventsDead[EventID][EventPriority][EventObject]then -self.Events[EventID][EventPriority][EventObject]=self.EventsDead[EventID][EventPriority][EventObject] -end -end -end -end -end -end -function EVENT:RemoveAll(EventClass) -local EventClassName=EventClass:GetClassNameAndID() -local EventPriority=EventClass:GetEventPriority() -for EventID,EventData in pairs(self.Events)do -self.Events[EventID][EventPriority][EventClass]=nil -end -return self -end -function EVENT:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EventID) -self:F2(EventTemplate.name) -for EventUnitID,EventUnit in pairs(EventTemplate.units)do -self:OnEventForUnit(EventUnit.name,EventFunction,EventClass,EventID) -end -return self -end -function EVENT:OnEventGeneric(EventFunction,EventClass,EventID) -self:F2({EventID,EventClass,EventFunction}) -local EventData=self:Init(EventID,EventClass) -EventData.EventFunction=EventFunction -return self -end -function EVENT:OnEventForUnit(UnitName,EventFunction,EventClass,EventID) -self:F2(UnitName) -local EventData=self:Init(EventID,EventClass) -EventData.EventUnit=true -EventData.EventFunction=EventFunction -return self -end -function EVENT:OnEventForGroup(GroupName,EventFunction,EventClass,EventID,...) -local Event=self:Init(EventID,EventClass) -Event.EventGroup=true -Event.EventFunction=EventFunction -Event.Params=arg -return self -end -do -function EVENT:OnBirthForTemplate(EventTemplate,EventFunction,EventClass) -self:F2(EventTemplate.name) -self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Birth) -return self -end -end -do -function EVENT:OnCrashForTemplate(EventTemplate,EventFunction,EventClass) -self:F2(EventTemplate.name) -self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Crash) -return self -end -end -do -function EVENT:OnDeadForTemplate(EventTemplate,EventFunction,EventClass) -self:F2(EventTemplate.name) -self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Dead) -return self -end -end -do -function EVENT:OnLandForTemplate(EventTemplate,EventFunction,EventClass) -self:F2(EventTemplate.name) -self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Land) -return self -end -end -do -function EVENT:OnTakeOffForTemplate(EventTemplate,EventFunction,EventClass) -self:F2(EventTemplate.name) -self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Takeoff) -return self -end -end -do -function EVENT:OnEngineShutDownForTemplate(EventTemplate,EventFunction,EventClass) -self:F2(EventTemplate.name) -self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.EngineShutdown) -return self -end -end -do -function EVENT:CreateEventNewCargo(Cargo) -self:F({Cargo}) -local Event={ -id=EVENTS.NewCargo, -time=timer.getTime(), -cargo=Cargo, -} -world.onEvent(Event) -end -function EVENT:CreateEventDeleteCargo(Cargo) -self:F({Cargo}) -local Event={ -id=EVENTS.DeleteCargo, -time=timer.getTime(), -cargo=Cargo, -} -world.onEvent(Event) -end -function EVENT:CreateEventNewZone(Zone) -self:F({Zone}) -local Event={ -id=EVENTS.NewZone, -time=timer.getTime(), -zone=Zone, -} -world.onEvent(Event) -end -function EVENT:CreateEventDeleteZone(Zone) -self:F({Zone}) -local Event={ -id=EVENTS.DeleteZone, -time=timer.getTime(), -zone=Zone, -} -world.onEvent(Event) -end -function EVENT:CreateEventNewZoneGoal(ZoneGoal) -self:F({ZoneGoal}) -local Event={ -id=EVENTS.NewZoneGoal, -time=timer.getTime(), -ZoneGoal=ZoneGoal, -} -world.onEvent(Event) -end -function EVENT:CreateEventDeleteZoneGoal(ZoneGoal) -self:F({ZoneGoal}) -local Event={ -id=EVENTS.DeleteZoneGoal, -time=timer.getTime(), -ZoneGoal=ZoneGoal, -} -world.onEvent(Event) -end -function EVENT:CreateEventPlayerEnterUnit(PlayerUnit) -self:F({PlayerUnit}) -local Event={ -id=EVENTS.PlayerEnterUnit, -time=timer.getTime(), -initiator=PlayerUnit:GetDCSObject() -} -world.onEvent(Event) -end -function EVENT:CreateEventPlayerEnterAircraft(PlayerUnit) -self:F({PlayerUnit}) -local Event={ -id=EVENTS.PlayerEnterAircraft, -time=timer.getTime(), -initiator=PlayerUnit:GetDCSObject() -} -world.onEvent(Event) -end -end -function EVENT:onEvent(Event) -local ErrorHandler=function(errmsg) -env.info("Error in SCHEDULER function:"..errmsg) -if BASE.Debug~=nil then -env.info(debug.traceback()) -end -return errmsg -end -local EventMeta=_EVENTMETA[Event.id] -if EventMeta then -if self and -self.Events and -self.Events[Event.id]and -self.MissionEnd==false and -(Event.initiator~=nil or(Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit))then -if Event.id and Event.id==EVENTS.MissionEnd then -self.MissionEnd=true -end -if Event.initiator then -Event.IniObjectCategory=Event.initiator:getCategory() -if Event.IniObjectCategory==Object.Category.UNIT then -Event.IniDCSUnit=Event.initiator -Event.IniDCSUnitName=Event.IniDCSUnit:getName() -Event.IniUnitName=Event.IniDCSUnitName -Event.IniDCSGroup=Event.IniDCSUnit:getGroup() -Event.IniUnit=UNIT:FindByName(Event.IniDCSUnitName) -if not Event.IniUnit then -Event.IniUnit=CLIENT:FindByName(Event.IniDCSUnitName,'',true) -end -Event.IniDCSGroupName="" -if Event.IniDCSGroup and Event.IniDCSGroup:isExist()then -Event.IniDCSGroupName=Event.IniDCSGroup:getName() -Event.IniGroup=GROUP:FindByName(Event.IniDCSGroupName) -Event.IniGroupName=Event.IniDCSGroupName -end -Event.IniPlayerName=Event.IniDCSUnit:getPlayerName() -Event.IniCoalition=Event.IniDCSUnit:getCoalition() -Event.IniTypeName=Event.IniDCSUnit:getTypeName() -Event.IniCategory=Event.IniDCSUnit:getDesc().category -end -if Event.IniObjectCategory==Object.Category.STATIC then -if Event.id==31 then -Event.IniDCSUnit=Event.initiator -local ID=Event.initiator.id_ -Event.IniDCSUnitName=string.format("Ejected Pilot ID %s",tostring(ID)) -Event.IniUnitName=Event.IniDCSUnitName -Event.IniCoalition=0 -Event.IniCategory=0 -Event.IniTypeName="Ejected Pilot" -elseif Event.id==33 then -Event.IniDCSUnit=Event.initiator -local ID=Event.initiator.id_ -Event.IniDCSUnitName=string.format("Ejection Seat ID %s",tostring(ID)) -Event.IniUnitName=Event.IniDCSUnitName -Event.IniCoalition=0 -Event.IniCategory=0 -Event.IniTypeName="Ejection Seat" -else -Event.IniDCSUnit=Event.initiator -Event.IniDCSUnitName=Event.IniDCSUnit:getName() -Event.IniUnitName=Event.IniDCSUnitName -Event.IniUnit=STATIC:FindByName(Event.IniDCSUnitName,false) -Event.IniCoalition=Event.IniDCSUnit:getCoalition() -Event.IniCategory=Event.IniDCSUnit:getDesc().category -Event.IniTypeName=Event.IniDCSUnit:getTypeName() -end -end -if Event.IniObjectCategory==Object.Category.CARGO then -Event.IniDCSUnit=Event.initiator -Event.IniDCSUnitName=Event.IniDCSUnit:getName() -Event.IniUnitName=Event.IniDCSUnitName -Event.IniUnit=CARGO:FindByName(Event.IniDCSUnitName) -Event.IniCoalition=Event.IniDCSUnit:getCoalition() -Event.IniCategory=Event.IniDCSUnit:getDesc().category -Event.IniTypeName=Event.IniDCSUnit:getTypeName() -end -if Event.IniObjectCategory==Object.Category.SCENERY then -Event.IniDCSUnit=Event.initiator -Event.IniDCSUnitName=Event.IniDCSUnit:getName() -Event.IniUnitName=Event.IniDCSUnitName -Event.IniUnit=SCENERY:Register(Event.IniDCSUnitName,Event.initiator) -Event.IniCategory=Event.IniDCSUnit:getDesc().category -Event.IniTypeName=Event.initiator:isExist()and Event.IniDCSUnit:getTypeName()or"SCENERY" -end -if Event.IniObjectCategory==Object.Category.BASE then -Event.IniDCSUnit=Event.initiator -Event.IniDCSUnitName=Event.IniDCSUnit:getName() -Event.IniUnitName=Event.IniDCSUnitName -Event.IniUnit=AIRBASE:FindByName(Event.IniDCSUnitName) -Event.IniCoalition=Event.IniDCSUnit:getCoalition() -Event.IniCategory=Event.IniDCSUnit:getDesc().category -Event.IniTypeName=Event.IniDCSUnit:getTypeName() -end -end -if Event.target then -Event.TgtObjectCategory=Event.target:getCategory() -if Event.TgtObjectCategory==Object.Category.UNIT then -Event.TgtDCSUnit=Event.target -Event.TgtDCSGroup=Event.TgtDCSUnit:getGroup() -Event.TgtDCSUnitName=Event.TgtDCSUnit:getName() -Event.TgtUnitName=Event.TgtDCSUnitName -Event.TgtUnit=UNIT:FindByName(Event.TgtDCSUnitName) -Event.TgtDCSGroupName="" -if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist()then -Event.TgtDCSGroupName=Event.TgtDCSGroup:getName() -Event.TgtGroup=GROUP:FindByName(Event.TgtDCSGroupName) -Event.TgtGroupName=Event.TgtDCSGroupName -end -Event.TgtPlayerName=Event.TgtDCSUnit:getPlayerName() -Event.TgtCoalition=Event.TgtDCSUnit:getCoalition() -Event.TgtCategory=Event.TgtDCSUnit:getDesc().category -Event.TgtTypeName=Event.TgtDCSUnit:getTypeName() -end -if Event.TgtObjectCategory==Object.Category.STATIC then -BASE:T({StaticTgtEvent=Event.id}) -Event.TgtDCSUnit=Event.target -if Event.target:isExist()and Event.id~=33 then -Event.TgtDCSUnitName=Event.TgtDCSUnit:getName() -Event.TgtUnitName=Event.TgtDCSUnitName -Event.TgtUnit=STATIC:FindByName(Event.TgtDCSUnitName,false) -Event.TgtCoalition=Event.TgtDCSUnit:getCoalition() -Event.TgtCategory=Event.TgtDCSUnit:getDesc().category -Event.TgtTypeName=Event.TgtDCSUnit:getTypeName() -else -Event.TgtDCSUnitName=string.format("No target object for Event ID %s",tostring(Event.id)) -Event.TgtUnitName=Event.TgtDCSUnitName -Event.TgtUnit=nil -Event.TgtCoalition=0 -Event.TgtCategory=0 -if Event.id==6 then -Event.TgtTypeName="Ejected Pilot" -Event.TgtDCSUnitName=string.format("Ejected Pilot ID %s",tostring(Event.IniDCSUnitName)) -Event.TgtUnitName=Event.TgtDCSUnitName -elseif Event.id==33 then -Event.TgtTypeName="Ejection Seat" -Event.TgtDCSUnitName=string.format("Ejection Seat ID %s",tostring(Event.IniDCSUnitName)) -Event.TgtUnitName=Event.TgtDCSUnitName -else -Event.TgtTypeName="Static" -end -end -end -if Event.TgtObjectCategory==Object.Category.SCENERY then -Event.TgtDCSUnit=Event.target -Event.TgtDCSUnitName=Event.TgtDCSUnit:getName() -Event.TgtUnitName=Event.TgtDCSUnitName -Event.TgtUnit=SCENERY:Register(Event.TgtDCSUnitName,Event.target) -Event.TgtCategory=Event.TgtDCSUnit:getDesc().category -Event.TgtTypeName=Event.TgtDCSUnit:getTypeName() -end -end -if Event.weapon then -Event.Weapon=Event.weapon -Event.WeaponName=Event.Weapon:getTypeName() -Event.WeaponUNIT=CLIENT:Find(Event.Weapon,'',true) -Event.WeaponPlayerName=Event.WeaponUNIT and Event.Weapon:getPlayerName() -Event.WeaponCoalition=Event.WeaponUNIT and Event.Weapon:getCoalition() -Event.WeaponCategory=Event.WeaponUNIT and Event.Weapon:getDesc().category -Event.WeaponTypeName=Event.WeaponUNIT and Event.Weapon:getTypeName() -end -if Event.place then -if Event.id==EVENTS.LandingAfterEjection then -else -Event.Place=AIRBASE:Find(Event.place) -Event.PlaceName=Event.Place:GetName() -end -end -if Event.idx then -Event.MarkID=Event.idx -Event.MarkVec3=Event.pos -Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos) -Event.MarkText=Event.text -Event.MarkCoalition=Event.coalition -Event.MarkGroupID=Event.groupID -end -if Event.cargo then -Event.Cargo=Event.cargo -Event.CargoName=Event.cargo.Name -end -if Event.zone then -Event.Zone=Event.zone -Event.ZoneName=Event.zone.ZoneName -end -local PriorityOrder=EventMeta.Order -local PriorityBegin=PriorityOrder==-1 and 5 or 1 -local PriorityEnd=PriorityOrder==-1 and 1 or 5 -if Event.IniObjectCategory~=Object.Category.STATIC then -self:F({EventMeta.Text,Event,Event.IniDCSUnitName,Event.TgtDCSUnitName,PriorityOrder}) -end -for EventPriority=PriorityBegin,PriorityEnd,PriorityOrder do -if self.Events[Event.id][EventPriority]then -for EventClass,EventData in pairs(self.Events[Event.id][EventPriority])do -Event.IniGroup=GROUP:FindByName(Event.IniDCSGroupName) -Event.TgtGroup=GROUP:FindByName(Event.TgtDCSGroupName) -if EventData.EventUnit then -if EventClass:IsAlive()or -Event.id==EVENTS.PlayerEnterUnit or -Event.id==EVENTS.Crash or -Event.id==EVENTS.Dead or -Event.id==EVENTS.RemoveUnit then -local UnitName=EventClass:GetName() -if(EventMeta.Side=="I"and UnitName==Event.IniDCSUnitName)or -(EventMeta.Side=="T"and UnitName==Event.TgtDCSUnitName)then -if EventData.EventFunction then -if Event.IniObjectCategory~=3 then -self:F({"Calling EventFunction for UNIT ",EventClass:GetClassNameAndID(),", Unit ",Event.IniUnitName,EventPriority}) -end -local Result,Value=xpcall( -function() -return EventData.EventFunction(EventClass,Event) -end,ErrorHandler) -else -local EventFunction=EventClass[EventMeta.Event] -if EventFunction and type(EventFunction)=="function"then -if Event.IniObjectCategory~=3 then -self:F({"Calling "..EventMeta.Event.." for Class ",EventClass:GetClassNameAndID(),EventPriority}) -end -local Result,Value=xpcall( -function() -return EventFunction(EventClass,Event) -end,ErrorHandler) -end -end -end -else -self:RemoveEvent(EventClass,Event.id) -end -else -if EventData.EventGroup then -if EventClass:IsAlive()or -Event.id==EVENTS.PlayerEnterUnit or -Event.id==EVENTS.Crash or -Event.id==EVENTS.Dead or -Event.id==EVENTS.RemoveUnit then -local GroupName=EventClass:GetName() -if(EventMeta.Side=="I"and GroupName==Event.IniDCSGroupName)or -(EventMeta.Side=="T"and GroupName==Event.TgtDCSGroupName)then -if EventData.EventFunction then -if Event.IniObjectCategory~=3 then -self:F({"Calling EventFunction for GROUP ",EventClass:GetClassNameAndID(),", Unit ",Event.IniUnitName,EventPriority}) -end -local Result,Value=xpcall( -function() -return EventData.EventFunction(EventClass,Event,unpack(EventData.Params)) -end,ErrorHandler) -else -local EventFunction=EventClass[EventMeta.Event] -if EventFunction and type(EventFunction)=="function"then -if Event.IniObjectCategory~=3 then -self:F({"Calling "..EventMeta.Event.." for GROUP ",EventClass:GetClassNameAndID(),EventPriority}) -end -local Result,Value=xpcall( -function() -return EventFunction(EventClass,Event,unpack(EventData.Params)) -end,ErrorHandler) -end -end -end -else -end -else -if not EventData.EventUnit then -if EventData.EventFunction then -if Event.IniObjectCategory~=3 then -self:F2({"Calling EventFunction for Class ",EventClass:GetClassNameAndID(),EventPriority}) -end -local Result,Value=xpcall( -function() -return EventData.EventFunction(EventClass,Event) -end,ErrorHandler) -else -local EventFunction=EventClass[EventMeta.Event] -if EventFunction and type(EventFunction)=="function"then -if Event.IniObjectCategory~=3 then -self:F2({"Calling "..EventMeta.Event.." for Class ",EventClass:GetClassNameAndID(),EventPriority}) -end -local Result,Value=xpcall( -function() -local Result,Value=EventFunction(EventClass,Event) -return Result,Value -end,ErrorHandler) -end -end -end -end -end -end -end -end -if Event.id==EVENTS.DeleteCargo then -Event.Cargo.NoDestroy=nil -end -else -self:T({EventMeta.Text,Event}) -end -else -self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?",tostring(Event.id))) -end -Event=nil -end -EVENTHANDLER={ -ClassName="EVENTHANDLER", -ClassID=0, -} -function EVENTHANDLER:New() -self=BASE:Inherit(self,BASE:New()) -return self -end -SETTINGS={ -ClassName="SETTINGS", -ShowPlayerMenu=true, -MenuShort=false, -MenuStatic=false, -} -SETTINGS.__Enum={} -SETTINGS.__Enum.Era={ -WWII=1, -Korea=2, -Cold=3, -Modern=4, -} -do -function SETTINGS:Set(PlayerName) -if PlayerName==nil then -local self=BASE:Inherit(self,BASE:New()) -self:SetMetric() -self:SetA2G_BR() -self:SetA2A_BRAA() -self:SetLL_Accuracy(3) -self:SetMGRS_Accuracy(5) -self:SetMessageTime(MESSAGE.Type.Briefing,180) -self:SetMessageTime(MESSAGE.Type.Detailed,60) -self:SetMessageTime(MESSAGE.Type.Information,30) -self:SetMessageTime(MESSAGE.Type.Overview,60) -self:SetMessageTime(MESSAGE.Type.Update,15) -self:SetEraModern() -return self -else -local Settings=_DATABASE:GetPlayerSettings(PlayerName) -if not Settings then -Settings=BASE:Inherit(self,BASE:New()) -_DATABASE:SetPlayerSettings(PlayerName,Settings) -end -return Settings -end -end -function SETTINGS:SetMenutextShort(onoff) -_SETTINGS.MenuShort=onoff -end -function SETTINGS:SetMenuStatic(onoff) -_SETTINGS.MenuStatic=onoff -end -function SETTINGS:SetMetric() -self.Metric=true -end -function SETTINGS:IsMetric() -return(self.Metric~=nil and self.Metric==true)or(self.Metric==nil and _SETTINGS:IsMetric()) -end -function SETTINGS:SetImperial() -self.Metric=false -end -function SETTINGS:IsImperial() -return(self.Metric~=nil and self.Metric==false)or(self.Metric==nil and _SETTINGS:IsMetric()) -end -function SETTINGS:SetLL_Accuracy(LL_Accuracy) -self.LL_Accuracy=LL_Accuracy -end -function SETTINGS:GetLL_DDM_Accuracy() -return self.LL_DDM_Accuracy or _SETTINGS:GetLL_DDM_Accuracy() -end -function SETTINGS:SetMGRS_Accuracy(MGRS_Accuracy) -self.MGRS_Accuracy=MGRS_Accuracy -end -function SETTINGS:GetMGRS_Accuracy() -return self.MGRS_Accuracy or _SETTINGS:GetMGRS_Accuracy() -end -function SETTINGS:SetMessageTime(MessageType,MessageTime) -self.MessageTypeTimings=self.MessageTypeTimings or{} -self.MessageTypeTimings[MessageType]=MessageTime -end -function SETTINGS:GetMessageTime(MessageType) -return(self.MessageTypeTimings and self.MessageTypeTimings[MessageType])or _SETTINGS:GetMessageTime(MessageType) -end -function SETTINGS:SetA2G_LL_DMS() -self.A2GSystem="LL DMS" -end -function SETTINGS:SetA2G_LL_DDM() -self.A2GSystem="LL DDM" -end -function SETTINGS:IsA2G_LL_DMS() -return(self.A2GSystem and self.A2GSystem=="LL DMS")or(not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS()) -end -function SETTINGS:IsA2G_LL_DDM() -return(self.A2GSystem and self.A2GSystem=="LL DDM")or(not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM()) -end -function SETTINGS:SetA2G_MGRS() -self.A2GSystem="MGRS" -end -function SETTINGS:IsA2G_MGRS() -return(self.A2GSystem and self.A2GSystem=="MGRS")or(not self.A2GSystem and _SETTINGS:IsA2G_MGRS()) -end -function SETTINGS:SetA2G_BR() -self.A2GSystem="BR" -end -function SETTINGS:IsA2G_BR() -return(self.A2GSystem and self.A2GSystem=="BR")or(not self.A2GSystem and _SETTINGS:IsA2G_BR()) -end -function SETTINGS:SetA2A_BRAA() -self.A2ASystem="BRAA" -end -function SETTINGS:IsA2A_BRAA() -return(self.A2ASystem and self.A2ASystem=="BRAA")or(not self.A2ASystem and _SETTINGS:IsA2A_BRAA()) -end -function SETTINGS:SetA2A_BULLS() -self.A2ASystem="BULLS" -end -function SETTINGS:IsA2A_BULLS() -return(self.A2ASystem and self.A2ASystem=="BULLS")or(not self.A2ASystem and _SETTINGS:IsA2A_BULLS()) -end -function SETTINGS:SetA2A_LL_DMS() -self.A2ASystem="LL DMS" -end -function SETTINGS:SetA2A_LL_DDM() -self.A2ASystem="LL DDM" -end -function SETTINGS:IsA2A_LL_DMS() -return(self.A2ASystem and self.A2ASystem=="LL DMS")or(not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS()) -end -function SETTINGS:IsA2A_LL_DDM() -return(self.A2ASystem and self.A2ASystem=="LL DDM")or(not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM()) -end -function SETTINGS:SetA2A_MGRS() -self.A2ASystem="MGRS" -end -function SETTINGS:IsA2A_MGRS() -return(self.A2ASystem and self.A2ASystem=="MGRS")or(not self.A2ASystem and _SETTINGS:IsA2A_MGRS()) -end -function SETTINGS:SetSystemMenu(MenuGroup,RootMenu) -local MenuText="System Settings" -local MenuTime=timer.getTime() -local SettingsMenu=MENU_GROUP:New(MenuGroup,MenuText,RootMenu):SetTime(MenuTime) -local text="A2G Coordinate System" -if _SETTINGS.MenuShort then -text="A2G Coordinates" -end -local A2GCoordinateMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime) -if not self:IsA2G_LL_DMS()then -local text="Lat/Lon Degree Min Sec (LL DMS)" -if _SETTINGS.MenuShort then -text="LL DMS" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"LL DMS"):SetTime(MenuTime) -end -if not self:IsA2G_LL_DDM()then -local text="Lat/Lon Degree Dec Min (LL DDM)" -if _SETTINGS.MenuShort then -text="LL DDM" -end -MENU_GROUP_COMMAND:New(MenuGroup,"Lat/Lon Degree Dec Min (LL DDM)",A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"LL DDM"):SetTime(MenuTime) -end -if self:IsA2G_LL_DDM()then -local text1="LL DDM Accuracy 1" -local text2="LL DDM Accuracy 2" -local text3="LL DDM Accuracy 3" -if _SETTINGS.MenuShort then -text1="LL DDM" -end -MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 1",A2GCoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 2",A2GCoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 3",A2GCoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime) -end -if not self:IsA2G_BR()then -local text="Bearing, Range (BR)" -if _SETTINGS.MenuShort then -text="BR" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"BR"):SetTime(MenuTime) -end -if not self:IsA2G_MGRS()then -local text="Military Grid (MGRS)" -if _SETTINGS.MenuShort then -text="MGRS" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"MGRS"):SetTime(MenuTime) -end -if self:IsA2G_MGRS()then -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 1",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 2",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 3",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 4",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,4):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 5",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,5):SetTime(MenuTime) -end -local text="A2A Coordinate System" -if _SETTINGS.MenuShort then -text="A2A Coordinates" -end -local A2ACoordinateMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime) -if not self:IsA2A_LL_DMS()then -local text="Lat/Lon Degree Min Sec (LL DMS)" -if _SETTINGS.MenuShort then -text="LL DMS" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"LL DMS"):SetTime(MenuTime) -end -if not self:IsA2A_LL_DDM()then -local text="Lat/Lon Degree Dec Min (LL DDM)" -if _SETTINGS.MenuShort then -text="LL DDM" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"LL DDM"):SetTime(MenuTime) -end -if self:IsA2A_LL_DDM()or self:IsA2A_LL_DMS()then -MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 0",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,0):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 1",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 2",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 3",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime) -end -if not self:IsA2A_BULLS()then -local text="Bullseye (BULLS)" -if _SETTINGS.MenuShort then -text="Bulls" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"BULLS"):SetTime(MenuTime) -end -if not self:IsA2A_BRAA()then -local text="Bearing Range Altitude Aspect (BRAA)" -if _SETTINGS.MenuShort then -text="BRAA" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"BRAA"):SetTime(MenuTime) -end -if not self:IsA2A_MGRS()then -local text="Military Grid (MGRS)" -if _SETTINGS.MenuShort then -text="MGRS" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"MGRS"):SetTime(MenuTime) -end -if self:IsA2A_MGRS()then -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 1",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 2",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 3",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 4",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,4):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 5",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,5):SetTime(MenuTime) -end -local text="Measures and Weights System" -if _SETTINGS.MenuShort then -text="Unit System" -end -local MetricsMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime) -if self:IsMetric()then -local text="Imperial (Miles,Feet)" -if _SETTINGS.MenuShort then -text="Imperial" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,MetricsMenu,self.MenuMWSystem,self,MenuGroup,RootMenu,false):SetTime(MenuTime) -end -if self:IsImperial()then -local text="Metric (Kilometers,Meters)" -if _SETTINGS.MenuShort then -text="Metric" -end -MENU_GROUP_COMMAND:New(MenuGroup,text,MetricsMenu,self.MenuMWSystem,self,MenuGroup,RootMenu,true):SetTime(MenuTime) -end -local text="Messages and Reports" -if _SETTINGS.MenuShort then -text="Messages & Reports" -end -local MessagesMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime) -local UpdateMessagesMenu=MENU_GROUP:New(MenuGroup,"Update Messages",MessagesMenu):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"Off",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,0):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"5 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,5):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"10 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,10):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,15):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,30):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,60):SetTime(MenuTime) -local InformationMessagesMenu=MENU_GROUP:New(MenuGroup,"Information Messages",MessagesMenu):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"5 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,5):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"10 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,10):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,15):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,30):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,60):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,120):SetTime(MenuTime) -local BriefingReportsMenu=MENU_GROUP:New(MenuGroup,"Briefing Reports",MessagesMenu):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,15):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,30):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,60):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,120):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"3 minutes",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,180):SetTime(MenuTime) -local OverviewReportsMenu=MENU_GROUP:New(MenuGroup,"Overview Reports",MessagesMenu):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,15):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,30):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,60):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,120):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"3 minutes",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,180):SetTime(MenuTime) -local DetailedReportsMenu=MENU_GROUP:New(MenuGroup,"Detailed Reports",MessagesMenu):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,15):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,30):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,60):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,120):SetTime(MenuTime) -MENU_GROUP_COMMAND:New(MenuGroup,"3 minutes",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,180):SetTime(MenuTime) -SettingsMenu:Remove(MenuTime) -return self -end -function SETTINGS:SetPlayerMenuOn() -self.ShowPlayerMenu=true -end -function SETTINGS:SetPlayerMenuOff() -self.ShowPlayerMenu=false -end -function SETTINGS:SetPlayerMenu(PlayerUnit) -if _SETTINGS.ShowPlayerMenu==true then -local PlayerGroup=PlayerUnit:GetGroup() -local PlayerName=PlayerUnit:GetPlayerName() -local PlayerNames=PlayerGroup:GetPlayerNames() -local PlayerMenu=MENU_GROUP:New(PlayerGroup,'Settings "'..PlayerName..'"') -self.PlayerMenu=PlayerMenu -self:I(string.format("Setting menu for player %s",tostring(PlayerName))) -local submenu=MENU_GROUP:New(PlayerGroup,"LL Accuracy",PlayerMenu) -MENU_GROUP_COMMAND:New(PlayerGroup,"LL 0 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,0) -MENU_GROUP_COMMAND:New(PlayerGroup,"LL 1 Decimal",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1) -MENU_GROUP_COMMAND:New(PlayerGroup,"LL 2 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2) -MENU_GROUP_COMMAND:New(PlayerGroup,"LL 3 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3) -MENU_GROUP_COMMAND:New(PlayerGroup,"LL 4 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,4) -local submenu=MENU_GROUP:New(PlayerGroup,"MGRS Accuracy",PlayerMenu) -MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 0",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,0) -MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 1",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1) -MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 2",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2) -MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 3",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3) -MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 4",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,4) -MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 5",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,5) -local text="A2G Coordinate System" -if _SETTINGS.MenuShort then -text="A2G Coordinates" -end -local A2GCoordinateMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu) -if not self:IsA2G_LL_DMS()or _SETTINGS.MenuStatic then -local text="Lat/Lon Degree Min Sec (LL DMS)" -if _SETTINGS.MenuShort then -text="A2G LL DMS" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DMS") -end -if not self:IsA2G_LL_DDM()or _SETTINGS.MenuStatic then -local text="Lat/Lon Degree Dec Min (LL DDM)" -if _SETTINGS.MenuShort then -text="A2G LL DDM" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DDM") -end -if not self:IsA2G_BR()or _SETTINGS.MenuStatic then -local text="Bearing, Range (BR)" -if _SETTINGS.MenuShort then -text="A2G BR" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"BR") -end -if not self:IsA2G_MGRS()or _SETTINGS.MenuStatic then -local text="Military Grid (MGRS)" -if _SETTINGS.MenuShort then -text="A2G MGRS" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"MGRS") -end -local text="A2A Coordinate System" -if _SETTINGS.MenuShort then -text="A2A Coordinates" -end -local A2ACoordinateMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu) -if not self:IsA2A_LL_DMS()or _SETTINGS.MenuStatic then -local text="Lat/Lon Degree Min Sec (LL DMS)" -if _SETTINGS.MenuShort then -text="A2A LL DMS" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DMS") -end -if not self:IsA2A_LL_DDM()or _SETTINGS.MenuStatic then -local text="Lat/Lon Degree Dec Min (LL DDM)" -if _SETTINGS.MenuShort then -text="A2A LL DDM" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DDM") -end -if not self:IsA2A_BULLS()or _SETTINGS.MenuStatic then -local text="Bullseye (BULLS)" -if _SETTINGS.MenuShort then -text="A2A BULLS" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"BULLS") -end -if not self:IsA2A_BRAA()or _SETTINGS.MenuStatic then -local text="Bearing Range Altitude Aspect (BRAA)" -if _SETTINGS.MenuShort then -text="A2A BRAA" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"BRAA") -end -if not self:IsA2A_MGRS()or _SETTINGS.MenuStatic then -local text="Military Grid (MGRS)" -if _SETTINGS.MenuShort then -text="A2A MGRS" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"MGRS") -end -local text="Measures and Weights System" -if _SETTINGS.MenuShort then -text="Unit System" -end -local MetricsMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu) -if self:IsMetric()or _SETTINGS.MenuStatic then -local text="Imperial (Miles,Feet)" -if _SETTINGS.MenuShort then -text="Imperial" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,MetricsMenu,self.MenuGroupMWSystem,self,PlayerUnit,PlayerGroup,PlayerName,false) -end -if self:IsImperial()or _SETTINGS.MenuStatic then -local text="Metric (Kilometers,Meters)" -if _SETTINGS.MenuShort then -text="Metric" -end -MENU_GROUP_COMMAND:New(PlayerGroup,text,MetricsMenu,self.MenuGroupMWSystem,self,PlayerUnit,PlayerGroup,PlayerName,true) -end -local text="Messages and Reports" -if _SETTINGS.MenuShort then -text="Messages & Reports" -end -local MessagesMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu) -local UpdateMessagesMenu=MENU_GROUP:New(PlayerGroup,"Update Messages",MessagesMenu) -MENU_GROUP_COMMAND:New(PlayerGroup,"Updates Off",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,0) -MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 5 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,5) -MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 10 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,10) -MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 15 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,15) -MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 30 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,30) -MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 1 min",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,60) -local InformationMessagesMenu=MENU_GROUP:New(PlayerGroup,"Info Messages",MessagesMenu) -MENU_GROUP_COMMAND:New(PlayerGroup,"Info 5 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,5) -MENU_GROUP_COMMAND:New(PlayerGroup,"Info 10 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,10) -MENU_GROUP_COMMAND:New(PlayerGroup,"Info 15 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,15) -MENU_GROUP_COMMAND:New(PlayerGroup,"Info 30 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,30) -MENU_GROUP_COMMAND:New(PlayerGroup,"Info 1 min",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,60) -MENU_GROUP_COMMAND:New(PlayerGroup,"Info 2 min",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,120) -local BriefingReportsMenu=MENU_GROUP:New(PlayerGroup,"Briefing Reports",MessagesMenu) -MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 15 sec",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,15) -MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 30 sec",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,30) -MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 1 min",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,60) -MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 2 min",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,120) -MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 3 min",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,180) -local OverviewReportsMenu=MENU_GROUP:New(PlayerGroup,"Overview Reports",MessagesMenu) -MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 15 sec",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,15) -MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 30 sec",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,30) -MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 1 min",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,60) -MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 2 min",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,120) -MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 3 min",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,180) -local DetailedReportsMenu=MENU_GROUP:New(PlayerGroup,"Detailed Reports",MessagesMenu) -MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 15 sec",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,15) -MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 30 sec",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,30) -MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 1 min",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,60) -MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 2 min",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,120) -MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 3 min",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,180) -end -return self -end -function SETTINGS:RemovePlayerMenu(PlayerUnit) -if self.PlayerMenu then -self.PlayerMenu:Remove() -self.PlayerMenu=nil -end -return self -end -function SETTINGS:A2GMenuSystem(MenuGroup,RootMenu,A2GSystem) -self.A2GSystem=A2GSystem -MESSAGE:New(string.format("Settings: Default A2G coordinate system set to %s for all players!",A2GSystem),5):ToAll() -self:SetSystemMenu(MenuGroup,RootMenu) -end -function SETTINGS:A2AMenuSystem(MenuGroup,RootMenu,A2ASystem) -self.A2ASystem=A2ASystem -MESSAGE:New(string.format("Settings: Default A2A coordinate system set to %s for all players!",A2ASystem),5):ToAll() -self:SetSystemMenu(MenuGroup,RootMenu) -end -function SETTINGS:MenuLL_DDM_Accuracy(MenuGroup,RootMenu,LL_Accuracy) -self.LL_Accuracy=LL_Accuracy -MESSAGE:New(string.format("Settings: Default LL accuracy set to %s for all players!",LL_Accuracy),5):ToAll() -self:SetSystemMenu(MenuGroup,RootMenu) -end -function SETTINGS:MenuMGRS_Accuracy(MenuGroup,RootMenu,MGRS_Accuracy) -self.MGRS_Accuracy=MGRS_Accuracy -MESSAGE:New(string.format("Settings: Default MGRS accuracy set to %s for all players!",MGRS_Accuracy),5):ToAll() -self:SetSystemMenu(MenuGroup,RootMenu) -end -function SETTINGS:MenuMWSystem(MenuGroup,RootMenu,MW) -self.Metric=MW -MESSAGE:New(string.format("Settings: Default measurement format set to %s for all players!",MW and"Metric"or"Imperial"),5):ToAll() -self:SetSystemMenu(MenuGroup,RootMenu) -end -function SETTINGS:MenuMessageTimingsSystem(MenuGroup,RootMenu,MessageType,MessageTime) -self:SetMessageTime(MessageType,MessageTime) -MESSAGE:New(string.format("Settings: Default message time set for %s to %d.",MessageType,MessageTime),5):ToAll() -end -do -function SETTINGS:MenuGroupA2GSystem(PlayerUnit,PlayerGroup,PlayerName,A2GSystem) -BASE:E({self,PlayerUnit:GetName(),A2GSystem}) -self.A2GSystem=A2GSystem -MESSAGE:New(string.format("Settings: A2G format set to %s for player %s.",A2GSystem,PlayerName),5):ToGroup(PlayerGroup) -if _SETTINGS.MenuStatic==false then -self:RemovePlayerMenu(PlayerUnit) -self:SetPlayerMenu(PlayerUnit) -end -end -function SETTINGS:MenuGroupA2ASystem(PlayerUnit,PlayerGroup,PlayerName,A2ASystem) -self.A2ASystem=A2ASystem -MESSAGE:New(string.format("Settings: A2A format set to %s for player %s.",A2ASystem,PlayerName),5):ToGroup(PlayerGroup) -if _SETTINGS.MenuStatic==false then -self:RemovePlayerMenu(PlayerUnit) -self:SetPlayerMenu(PlayerUnit) -end -end -function SETTINGS:MenuGroupLL_DDM_AccuracySystem(PlayerUnit,PlayerGroup,PlayerName,LL_Accuracy) -self.LL_Accuracy=LL_Accuracy -MESSAGE:New(string.format("Settings: LL format accuracy set to %d decimal places for player %s.",LL_Accuracy,PlayerName),5):ToGroup(PlayerGroup) -if _SETTINGS.MenuStatic==false then -self:RemovePlayerMenu(PlayerUnit) -self:SetPlayerMenu(PlayerUnit) -end -end -function SETTINGS:MenuGroupMGRS_AccuracySystem(PlayerUnit,PlayerGroup,PlayerName,MGRS_Accuracy) -self.MGRS_Accuracy=MGRS_Accuracy -MESSAGE:New(string.format("Settings: MGRS format accuracy set to %d for player %s.",MGRS_Accuracy,PlayerName),5):ToGroup(PlayerGroup) -if _SETTINGS.MenuStatic==false then -self:RemovePlayerMenu(PlayerUnit) -self:SetPlayerMenu(PlayerUnit) -end -end -function SETTINGS:MenuGroupMWSystem(PlayerUnit,PlayerGroup,PlayerName,MW) -self.Metric=MW -MESSAGE:New(string.format("Settings: Measurement format set to %s for player %s.",MW and"Metric"or"Imperial",PlayerName),5):ToGroup(PlayerGroup) -if _SETTINGS.MenuStatic==false then -self:RemovePlayerMenu(PlayerUnit) -self:SetPlayerMenu(PlayerUnit) -end -end -function SETTINGS:MenuGroupMessageTimingsSystem(PlayerUnit,PlayerGroup,PlayerName,MessageType,MessageTime) -self:SetMessageTime(MessageType,MessageTime) -MESSAGE:New(string.format("Settings: Default message time set for %s to %d.",MessageType,MessageTime),5):ToGroup(PlayerGroup) -end -end -function SETTINGS:SetEraWWII() -self.Era=SETTINGS.__Enum.Era.WWII -end -function SETTINGS:SetEraKorea() -self.Era=SETTINGS.__Enum.Era.Korea -end -function SETTINGS:SetEraCold() -self.Era=SETTINGS.__Enum.Era.Cold -end -function SETTINGS:SetEraModern() -self.Era=SETTINGS.__Enum.Era.Modern -end -end -MENU_INDEX={} -MENU_INDEX.MenuMission={} -MENU_INDEX.MenuMission.Menus={} -MENU_INDEX.Coalition={} -MENU_INDEX.Coalition[coalition.side.BLUE]={} -MENU_INDEX.Coalition[coalition.side.BLUE].Menus={} -MENU_INDEX.Coalition[coalition.side.RED]={} -MENU_INDEX.Coalition[coalition.side.RED].Menus={} -MENU_INDEX.Group={} -function MENU_INDEX:ParentPath(ParentMenu,MenuText) -local Path=ParentMenu and"@"..table.concat(ParentMenu.MenuPath or{},"@")or"" -if ParentMenu then -if ParentMenu:IsInstanceOf("MENU_GROUP")or ParentMenu:IsInstanceOf("MENU_GROUP_COMMAND")then -local GroupName=ParentMenu.Group:GetName() -if not self.Group[GroupName].Menus[Path]then -BASE:E({Path=Path,GroupName=GroupName}) -error("Parent path not found in menu index for group menu") -return nil -end -elseif ParentMenu:IsInstanceOf("MENU_COALITION")or ParentMenu:IsInstanceOf("MENU_COALITION_COMMAND")then -local Coalition=ParentMenu.Coalition -if not self.Coalition[Coalition].Menus[Path]then -BASE:E({Path=Path,Coalition=Coalition}) -error("Parent path not found in menu index for coalition menu") -return nil -end -elseif ParentMenu:IsInstanceOf("MENU_MISSION")or ParentMenu:IsInstanceOf("MENU_MISSION_COMMAND")then -if not self.MenuMission.Menus[Path]then -BASE:E({Path=Path}) -error("Parent path not found in menu index for mission menu") -return nil -end -end -end -Path=Path.."@"..MenuText -return Path -end -function MENU_INDEX:PrepareMission() -self.MenuMission.Menus=self.MenuMission.Menus or{} -end -function MENU_INDEX:PrepareCoalition(CoalitionSide) -self.Coalition[CoalitionSide]=self.Coalition[CoalitionSide]or{} -self.Coalition[CoalitionSide].Menus=self.Coalition[CoalitionSide].Menus or{} -end -function MENU_INDEX:PrepareGroup(Group) -if Group and Group:IsAlive()~=nil then -local GroupName=Group:GetName() -self.Group[GroupName]=self.Group[GroupName]or{} -self.Group[GroupName].Menus=self.Group[GroupName].Menus or{} -end -end -function MENU_INDEX:HasMissionMenu(Path) -return self.MenuMission.Menus[Path] -end -function MENU_INDEX:SetMissionMenu(Path,Menu) -self.MenuMission.Menus[Path]=Menu -end -function MENU_INDEX:ClearMissionMenu(Path) -self.MenuMission.Menus[Path]=nil -end -function MENU_INDEX:HasCoalitionMenu(Coalition,Path) -return self.Coalition[Coalition].Menus[Path] -end -function MENU_INDEX:SetCoalitionMenu(Coalition,Path,Menu) -self.Coalition[Coalition].Menus[Path]=Menu -end -function MENU_INDEX:ClearCoalitionMenu(Coalition,Path) -self.Coalition[Coalition].Menus[Path]=nil -end -function MENU_INDEX:HasGroupMenu(Group,Path) -if Group and Group:IsAlive()then -local MenuGroupName=Group:GetName() -return self.Group[MenuGroupName].Menus[Path] -end -return nil -end -function MENU_INDEX:SetGroupMenu(Group,Path,Menu) -local MenuGroupName=Group:GetName() -Group:F({MenuGroupName=MenuGroupName,Path=Path}) -self.Group[MenuGroupName].Menus[Path]=Menu -end -function MENU_INDEX:ClearGroupMenu(Group,Path) -local MenuGroupName=Group:GetName() -self.Group[MenuGroupName].Menus[Path]=nil -end -function MENU_INDEX:Refresh(Group) -for MenuID,Menu in pairs(self.MenuMission.Menus)do -Menu:Refresh() -end -for MenuID,Menu in pairs(self.Coalition[coalition.side.BLUE].Menus)do -Menu:Refresh() -end -for MenuID,Menu in pairs(self.Coalition[coalition.side.RED].Menus)do -Menu:Refresh() -end -local GroupName=Group:GetName() -for MenuID,Menu in pairs(self.Group[GroupName].Menus)do -Menu:Refresh() -end -end -do -MENU_BASE={ -ClassName="MENU_BASE", -MenuPath=nil, -MenuText="", -MenuParentPath=nil -} -function MENU_BASE:New(MenuText,ParentMenu) -local MenuParentPath={} -if ParentMenu~=nil then -MenuParentPath=ParentMenu.MenuPath -end -local self=BASE:Inherit(self,BASE:New()) -self.MenuPath=nil -self.MenuText=MenuText -self.ParentMenu=ParentMenu -self.MenuParentPath=MenuParentPath -self.Path=(self.ParentMenu and"@"..table.concat(self.MenuParentPath or{},"@")or"").."@"..self.MenuText -self.Menus={} -self.MenuCount=0 -self.MenuStamp=timer.getTime() -self.MenuRemoveParent=false -if self.ParentMenu then -self.ParentMenu.Menus=self.ParentMenu.Menus or{} -self.ParentMenu.Menus[MenuText]=self -end -return self -end -function MENU_BASE:SetParentMenu(MenuText,Menu) -if self.ParentMenu then -self.ParentMenu.Menus=self.ParentMenu.Menus or{} -self.ParentMenu.Menus[MenuText]=Menu -self.ParentMenu.MenuCount=self.ParentMenu.MenuCount+1 -end -end -function MENU_BASE:ClearParentMenu(MenuText) -if self.ParentMenu and self.ParentMenu.Menus[MenuText]then -self.ParentMenu.Menus[MenuText]=nil -self.ParentMenu.MenuCount=self.ParentMenu.MenuCount-1 -if self.ParentMenu.MenuCount==0 then -end -end -end -function MENU_BASE:SetRemoveParent(RemoveParent) -self.MenuRemoveParent=RemoveParent -return self -end -function MENU_BASE:GetMenu(MenuText) -return self.Menus[MenuText] -end -function MENU_BASE:SetStamp(MenuStamp) -self.MenuStamp=MenuStamp -return self -end -function MENU_BASE:GetStamp() -return timer.getTime() -end -function MENU_BASE:SetTime(MenuStamp) -self.MenuStamp=MenuStamp -return self -end -function MENU_BASE:SetTag(MenuTag) -self.MenuTag=MenuTag -return self -end -end -do -MENU_COMMAND_BASE={ -ClassName="MENU_COMMAND_BASE", -CommandMenuFunction=nil, -CommandMenuArgument=nil, -MenuCallHandler=nil, -} -function MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,CommandMenuArguments) -local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) -local ErrorHandler=function(errmsg) -env.info("MOOSE error in MENU COMMAND function: "..errmsg) -if BASE.Debug~=nil then -env.info(BASE.Debug.traceback()) -end -return errmsg -end -self:SetCommandMenuFunction(CommandMenuFunction) -self:SetCommandMenuArguments(CommandMenuArguments) -self.MenuCallHandler=function() -local function MenuFunction() -return self.CommandMenuFunction(unpack(self.CommandMenuArguments)) -end -local Status,Result=xpcall(MenuFunction,ErrorHandler) -end -return self -end -function MENU_COMMAND_BASE:SetCommandMenuFunction(CommandMenuFunction) -self.CommandMenuFunction=CommandMenuFunction -return self -end -function MENU_COMMAND_BASE:SetCommandMenuArguments(CommandMenuArguments) -self.CommandMenuArguments=CommandMenuArguments -return self -end -end -do -MENU_MISSION={ -ClassName="MENU_MISSION" -} -function MENU_MISSION:New(MenuText,ParentMenu) -MENU_INDEX:PrepareMission() -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local MissionMenu=MENU_INDEX:HasMissionMenu(Path) -if MissionMenu then -return MissionMenu -else -local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) -MENU_INDEX:SetMissionMenu(Path,self) -self.MenuPath=missionCommands.addSubMenu(self.MenuText,self.MenuParentPath) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_MISSION:Refresh() -do -missionCommands.removeItem(self.MenuPath) -self.MenuPath=missionCommands.addSubMenu(self.MenuText,self.MenuParentPath) -end -end -function MENU_MISSION:RemoveSubMenus() -for MenuID,Menu in pairs(self.Menus or{})do -Menu:Remove() -end -self.Menus=nil -end -function MENU_MISSION:Remove(MenuStamp,MenuTag) -MENU_INDEX:PrepareMission() -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local MissionMenu=MENU_INDEX:HasMissionMenu(Path) -if MissionMenu==self then -self:RemoveSubMenus() -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -self:F({Text=self.MenuText,Path=self.MenuPath}) -if self.MenuPath~=nil then -missionCommands.removeItem(self.MenuPath) -end -MENU_INDEX:ClearMissionMenu(self.Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_MISSION",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText}) -end -return self -end -end -do -MENU_MISSION_COMMAND={ -ClassName="MENU_MISSION_COMMAND" -} -function MENU_MISSION_COMMAND:New(MenuText,ParentMenu,CommandMenuFunction,...) -MENU_INDEX:PrepareMission() -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local MissionMenu=MENU_INDEX:HasMissionMenu(Path) -if MissionMenu then -MissionMenu:SetCommandMenuFunction(CommandMenuFunction) -MissionMenu:SetCommandMenuArguments(arg) -return MissionMenu -else -local self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg)) -MENU_INDEX:SetMissionMenu(Path,self) -self.MenuPath=missionCommands.addCommand(MenuText,self.MenuParentPath,self.MenuCallHandler) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_MISSION_COMMAND:Refresh() -do -missionCommands.removeItem(self.MenuPath) -missionCommands.addCommand(self.MenuText,self.MenuParentPath,self.MenuCallHandler) -end -end -function MENU_MISSION_COMMAND:Remove() -MENU_INDEX:PrepareMission() -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local MissionMenu=MENU_INDEX:HasMissionMenu(Path) -if MissionMenu==self then -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -self:F({Text=self.MenuText,Path=self.MenuPath}) -if self.MenuPath~=nil then -missionCommands.removeItem(self.MenuPath) -end -MENU_INDEX:ClearMissionMenu(self.Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_MISSION_COMMAND",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText}) -end -return self -end -end -do -MENU_COALITION={ -ClassName="MENU_COALITION" -} -function MENU_COALITION:New(Coalition,MenuText,ParentMenu) -MENU_INDEX:PrepareCoalition(Coalition) -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(Coalition,Path) -if CoalitionMenu then -return CoalitionMenu -else -local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) -MENU_INDEX:SetCoalitionMenu(Coalition,Path,self) -self.Coalition=Coalition -self.MenuPath=missionCommands.addSubMenuForCoalition(Coalition,MenuText,self.MenuParentPath) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_COALITION:Refresh() -do -missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath) -missionCommands.addSubMenuForCoalition(self.Coalition,self.MenuText,self.MenuParentPath) -end -end -function MENU_COALITION:RemoveSubMenus() -for MenuID,Menu in pairs(self.Menus or{})do -Menu:Remove() -end -self.Menus=nil -end -function MENU_COALITION:Remove(MenuStamp,MenuTag) -MENU_INDEX:PrepareCoalition(self.Coalition) -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(self.Coalition,Path) -if CoalitionMenu==self then -self:RemoveSubMenus() -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -self:F({Coalition=self.Coalition,Text=self.MenuText,Path=self.MenuPath}) -if self.MenuPath~=nil then -missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath) -end -MENU_INDEX:ClearCoalitionMenu(self.Coalition,Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_COALITION",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Coalition=self.Coalition}) -end -return self -end -end -do -MENU_COALITION_COMMAND={ -ClassName="MENU_COALITION_COMMAND" -} -function MENU_COALITION_COMMAND:New(Coalition,MenuText,ParentMenu,CommandMenuFunction,...) -MENU_INDEX:PrepareCoalition(Coalition) -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(Coalition,Path) -if CoalitionMenu then -CoalitionMenu:SetCommandMenuFunction(CommandMenuFunction) -CoalitionMenu:SetCommandMenuArguments(arg) -return CoalitionMenu -else -local self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg)) -MENU_INDEX:SetCoalitionMenu(Coalition,Path,self) -self.Coalition=Coalition -self.MenuPath=missionCommands.addCommandForCoalition(self.Coalition,MenuText,self.MenuParentPath,self.MenuCallHandler) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_COALITION_COMMAND:Refresh() -do -missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath) -missionCommands.addCommandForCoalition(self.Coalition,self.MenuText,self.MenuParentPath,self.MenuCallHandler) -end -end -function MENU_COALITION_COMMAND:Remove(MenuStamp,MenuTag) -MENU_INDEX:PrepareCoalition(self.Coalition) -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(self.Coalition,Path) -if CoalitionMenu==self then -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -self:F({Coalition=self.Coalition,Text=self.MenuText,Path=self.MenuPath}) -if self.MenuPath~=nil then -missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath) -end -MENU_INDEX:ClearCoalitionMenu(self.Coalition,Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_COALITION_COMMAND",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Coalition=self.Coalition}) -end -return self -end -end -do -local _MENUGROUPS={} -MENU_GROUP={ -ClassName="MENU_GROUP" -} -function MENU_GROUP:New(Group,MenuText,ParentMenu) -MENU_INDEX:PrepareGroup(Group) -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(Group,Path) -if GroupMenu then -return GroupMenu -else -self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) -MENU_INDEX:SetGroupMenu(Group,Path,self) -self.Group=Group -self.GroupID=Group:GetID() -self.MenuPath=missionCommands.addSubMenuForGroup(self.GroupID,MenuText,self.MenuParentPath) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_GROUP:Refresh() -do -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -missionCommands.addSubMenuForGroup(self.GroupID,self.MenuText,self.MenuParentPath) -for MenuText,Menu in pairs(self.Menus or{})do -Menu:Refresh() -end -end -end -function MENU_GROUP:RemoveSubMenus(MenuStamp,MenuTag) -for MenuText,Menu in pairs(self.Menus or{})do -Menu:Remove(MenuStamp,MenuTag) -end -self.Menus=nil -end -function MENU_GROUP:Remove(MenuStamp,MenuTag) -MENU_INDEX:PrepareGroup(self.Group) -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(self.Group,Path) -if GroupMenu==self then -self:RemoveSubMenus(MenuStamp,MenuTag) -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -if self.MenuPath~=nil then -self:F({Group=self.GroupID,Text=self.MenuText,Path=self.MenuPath}) -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -end -MENU_INDEX:ClearGroupMenu(self.Group,Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_GROUP",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Group=self.Group}) -return nil -end -return self -end -MENU_GROUP_COMMAND={ -ClassName="MENU_GROUP_COMMAND" -} -function MENU_GROUP_COMMAND:New(Group,MenuText,ParentMenu,CommandMenuFunction,...) -MENU_INDEX:PrepareGroup(Group) -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(Group,Path) -if GroupMenu then -GroupMenu:SetCommandMenuFunction(CommandMenuFunction) -GroupMenu:SetCommandMenuArguments(arg) -return GroupMenu -else -self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg)) -MENU_INDEX:SetGroupMenu(Group,Path,self) -self.Group=Group -self.GroupID=Group:GetID() -self.MenuPath=missionCommands.addCommandForGroup(self.GroupID,MenuText,self.MenuParentPath,self.MenuCallHandler) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_GROUP_COMMAND:Refresh() -do -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -missionCommands.addCommandForGroup(self.GroupID,self.MenuText,self.MenuParentPath,self.MenuCallHandler) -end -end -function MENU_GROUP_COMMAND:Remove(MenuStamp,MenuTag) -MENU_INDEX:PrepareGroup(self.Group) -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(self.Group,Path) -if GroupMenu==self then -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -if self.MenuPath~=nil then -self:F({Group=self.GroupID,Text=self.MenuText,Path=self.MenuPath}) -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -end -MENU_INDEX:ClearGroupMenu(self.Group,Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_GROUP_COMMAND",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Group=self.Group}) -end -return self -end -end -do -MENU_GROUP_DELAYED={ -ClassName="MENU_GROUP_DELAYED" -} -function MENU_GROUP_DELAYED:New(Group,MenuText,ParentMenu) -MENU_INDEX:PrepareGroup(Group) -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(Group,Path) -if GroupMenu then -return GroupMenu -else -self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) -MENU_INDEX:SetGroupMenu(Group,Path,self) -self.Group=Group -self.GroupID=Group:GetID() -if self.MenuParentPath then -self.MenuPath=UTILS.DeepCopy(self.MenuParentPath) -else -self.MenuPath={} -end -table.insert(self.MenuPath,self.MenuText) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_GROUP_DELAYED:Set() -do -if not self.MenuSet then -missionCommands.addSubMenuForGroup(self.GroupID,self.MenuText,self.MenuParentPath) -self.MenuSet=true -end -for MenuText,Menu in pairs(self.Menus or{})do -Menu:Set() -end -end -end -function MENU_GROUP_DELAYED:Refresh() -do -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -missionCommands.addSubMenuForGroup(self.GroupID,self.MenuText,self.MenuParentPath) -for MenuText,Menu in pairs(self.Menus or{})do -Menu:Refresh() -end -end -end -function MENU_GROUP_DELAYED:RemoveSubMenus(MenuStamp,MenuTag) -for MenuText,Menu in pairs(self.Menus or{})do -Menu:Remove(MenuStamp,MenuTag) -end -self.Menus=nil -end -function MENU_GROUP_DELAYED:Remove(MenuStamp,MenuTag) -MENU_INDEX:PrepareGroup(self.Group) -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(self.Group,Path) -if GroupMenu==self then -self:RemoveSubMenus(MenuStamp,MenuTag) -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -if self.MenuPath~=nil then -self:F({Group=self.GroupID,Text=self.MenuText,Path=self.MenuPath}) -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -end -MENU_INDEX:ClearGroupMenu(self.Group,Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_GROUP_DELAYED",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Group=self.Group}) -return nil -end -return self -end -MENU_GROUP_COMMAND_DELAYED={ -ClassName="MENU_GROUP_COMMAND_DELAYED" -} -function MENU_GROUP_COMMAND_DELAYED:New(Group,MenuText,ParentMenu,CommandMenuFunction,...) -MENU_INDEX:PrepareGroup(Group) -local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(Group,Path) -if GroupMenu then -GroupMenu:SetCommandMenuFunction(CommandMenuFunction) -GroupMenu:SetCommandMenuArguments(arg) -return GroupMenu -else -self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg)) -MENU_INDEX:SetGroupMenu(Group,Path,self) -self.Group=Group -self.GroupID=Group:GetID() -if self.MenuParentPath then -self.MenuPath=UTILS.DeepCopy(self.MenuParentPath) -else -self.MenuPath={} -end -table.insert(self.MenuPath,self.MenuText) -self:SetParentMenu(self.MenuText,self) -return self -end -end -function MENU_GROUP_COMMAND_DELAYED:Set() -do -if not self.MenuSet then -self.MenuPath=missionCommands.addCommandForGroup(self.GroupID,self.MenuText,self.MenuParentPath,self.MenuCallHandler) -self.MenuSet=true -end -end -end -function MENU_GROUP_COMMAND_DELAYED:Refresh() -do -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -missionCommands.addCommandForGroup(self.GroupID,self.MenuText,self.MenuParentPath,self.MenuCallHandler) -end -end -function MENU_GROUP_COMMAND_DELAYED:Remove(MenuStamp,MenuTag) -MENU_INDEX:PrepareGroup(self.Group) -local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText) -local GroupMenu=MENU_INDEX:HasGroupMenu(self.Group,Path) -if GroupMenu==self then -if not MenuStamp or self.MenuStamp~=MenuStamp then -if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then -if self.MenuPath~=nil then -self:F({Group=self.GroupID,Text=self.MenuText,Path=self.MenuPath}) -missionCommands.removeItemForGroup(self.GroupID,self.MenuPath) -end -MENU_INDEX:ClearGroupMenu(self.Group,Path) -self:ClearParentMenu(self.MenuText) -return nil -end -end -else -BASE:E({"Cannot Remove MENU_GROUP_COMMAND_DELAYED",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Group=self.Group}) -end -return self -end -end -ZONE_BASE={ -ClassName="ZONE_BASE", -ZoneName="", -ZoneProbability=1, -} -function ZONE_BASE:New(ZoneName) -local self=BASE:Inherit(self,FSM:New()) -self:F(ZoneName) -self.ZoneName=ZoneName -return self -end -function ZONE_BASE:GetName() -self:F2() -return self.ZoneName -end -function ZONE_BASE:SetName(ZoneName) -self:F2() -self.ZoneName=ZoneName -end -function ZONE_BASE:IsVec2InZone(Vec2) -self:F2(Vec2) -return false -end -function ZONE_BASE:IsVec3InZone(Vec3) -local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z}) -return InZone -end -function ZONE_BASE:IsCoordinateInZone(Coordinate) -local InZone=self:IsVec2InZone(Coordinate:GetVec2()) -return InZone -end -function ZONE_BASE:IsPointVec2InZone(PointVec2) -local InZone=self:IsVec2InZone(PointVec2:GetVec2()) -return InZone -end -function ZONE_BASE:IsPointVec3InZone(PointVec3) -local InZone=self:IsPointVec2InZone(PointVec3) -return InZone -end -function ZONE_BASE:GetVec2() -return nil -end -function ZONE_BASE:GetPointVec2() -self:F2(self.ZoneName) -local Vec2=self:GetVec2() -local PointVec2=POINT_VEC2:NewFromVec2(Vec2) -self:T2({PointVec2}) -return PointVec2 -end -function ZONE_BASE:GetVec3(Height) -self:F2(self.ZoneName) -Height=Height or 0 -local Vec2=self:GetVec2() -local Vec3={x=Vec2.x,y=Height and Height or land.getHeight(self:GetVec2()),z=Vec2.y} -self:T2({Vec3}) -return Vec3 -end -function ZONE_BASE:GetPointVec3(Height) -self:F2(self.ZoneName) -local Vec3=self:GetVec3(Height) -local PointVec3=POINT_VEC3:NewFromVec3(Vec3) -self:T2({PointVec3}) -return PointVec3 -end -function ZONE_BASE:GetCoordinate(Height) -self:F2(self.ZoneName) -local Vec3=self:GetVec3(Height) -if self.Coordinate then -self.Coordinate.x=Vec3.x -self.Coordinate.y=Vec3.y -self.Coordinate.z=Vec3.z -else -self.Coordinate=COORDINATE:NewFromVec3(Vec3) -end -return self.Coordinate -end -function ZONE_BASE:GetRandomVec2() -return nil -end -function ZONE_BASE:GetRandomPointVec2() -return nil -end -function ZONE_BASE:GetRandomPointVec3() -return nil -end -function ZONE_BASE:GetBoundingSquare() -return nil -end -function ZONE_BASE:BoundZone() -self:F2() -end -function ZONE_BASE:SmokeZone(SmokeColor) -self:F2(SmokeColor) -end -function ZONE_BASE:SetZoneProbability(ZoneProbability) -self:F({self:GetName(),ZoneProbability=ZoneProbability}) -self.ZoneProbability=ZoneProbability or 1 -return self -end -function ZONE_BASE:GetZoneProbability() -self:F2() -return self.ZoneProbability -end -function ZONE_BASE:GetZoneMaybe() -self:F2() -local Randomization=math.random() -if Randomization<=self.ZoneProbability then -return self -else -return nil -end -end -ZONE_RADIUS={ -ClassName="ZONE_RADIUS", -} -function ZONE_RADIUS:New(ZoneName,Vec2,Radius) -local self=BASE:Inherit(self,ZONE_BASE:New(ZoneName)) -self:F({ZoneName,Vec2,Radius}) -self.Radius=Radius -self.Vec2=Vec2 -return self -end -function ZONE_RADIUS:UpdateFromVec2(Vec2,Radius) -self.Vec2=Vec2 -if Radius then -self.Radius=Radius -end -return self -end -function ZONE_RADIUS:UpdateFromVec3(Vec3,Radius) -self.Vec2.x=Vec3.x -self.Vec2.y=Vec3.z -if Radius then -self.Radius=Radius -end -return self -end -function ZONE_RADIUS:MarkZone(Points) -local Point={} -local Vec2=self:GetVec2() -Points=Points and Points or 360 -local Angle -local RadialBase=math.pi*2 -for Angle=0,360,(360/Points)do -local Radial=Angle*RadialBase/360 -Point.x=Vec2.x+math.cos(Radial)*self:GetRadius() -Point.y=Vec2.y+math.sin(Radial)*self:GetRadius() -COORDINATE:NewFromVec2(Point):MarkToAll(self:GetName()) -end -end -function ZONE_RADIUS:BoundZone(Points,CountryID,UnBound) -local Point={} -local Vec2=self:GetVec2() -Points=Points and Points or 360 -local Angle -local RadialBase=math.pi*2 -for Angle=0,360,(360/Points)do -local Radial=Angle*RadialBase/360 -Point.x=Vec2.x+math.cos(Radial)*self:GetRadius() -Point.y=Vec2.y+math.sin(Radial)*self:GetRadius() -local CountryName=_DATABASE.COUNTRY_NAME[CountryID] -local Tire={ -["country"]=CountryName, -["category"]="Fortifications", -["canCargo"]=false, -["shape_name"]="H-tyre_B_WF", -["type"]="Black_Tyre_WF", -["y"]=Point.y, -["x"]=Point.x, -["name"]=string.format("%s-Tire #%0d",self:GetName(),Angle), -["heading"]=0, -} -local Group=coalition.addStaticObject(CountryID,Tire) -if UnBound and UnBound==true then -Group:destroy() -end -end -return self -end -function ZONE_RADIUS:SmokeZone(SmokeColor,Points,AddHeight,AngleOffset) -self:F2(SmokeColor) -local Point={} -local Vec2=self:GetVec2() -AddHeight=AddHeight or 0 -AngleOffset=AngleOffset or 0 -Points=Points and Points or 360 -local Angle -local RadialBase=math.pi*2 -for Angle=0,360,360/Points do -local Radial=(Angle+AngleOffset)*RadialBase/360 -Point.x=Vec2.x+math.cos(Radial)*self:GetRadius() -Point.y=Vec2.y+math.sin(Radial)*self:GetRadius() -POINT_VEC2:New(Point.x,Point.y,AddHeight):Smoke(SmokeColor) -end -return self -end -function ZONE_RADIUS:FlareZone(FlareColor,Points,Azimuth,AddHeight) -self:F2({FlareColor,Azimuth}) -local Point={} -local Vec2=self:GetVec2() -AddHeight=AddHeight or 0 -Points=Points and Points or 360 -local Angle -local RadialBase=math.pi*2 -for Angle=0,360,360/Points do -local Radial=Angle*RadialBase/360 -Point.x=Vec2.x+math.cos(Radial)*self:GetRadius() -Point.y=Vec2.y+math.sin(Radial)*self:GetRadius() -POINT_VEC2:New(Point.x,Point.y,AddHeight):Flare(FlareColor,Azimuth) -end -return self -end -function ZONE_RADIUS:GetRadius() -self:F2(self.ZoneName) -self:T2({self.Radius}) -return self.Radius -end -function ZONE_RADIUS:SetRadius(Radius) -self:F2(self.ZoneName) -self.Radius=Radius -self:T2({self.Radius}) -return self.Radius -end -function ZONE_RADIUS:GetVec2() -self:F2(self.ZoneName) -self:T2({self.Vec2}) -return self.Vec2 -end -function ZONE_RADIUS:SetVec2(Vec2) -self:F2(self.ZoneName) -self.Vec2=Vec2 -self:T2({self.Vec2}) -return self.Vec2 -end -function ZONE_RADIUS:GetVec3(Height) -self:F2({self.ZoneName,Height}) -Height=Height or 0 -local Vec2=self:GetVec2() -local Vec3={x=Vec2.x,y=land.getHeight(self:GetVec2())+Height,z=Vec2.y} -self:T2({Vec3}) -return Vec3 -end -function ZONE_RADIUS:Scan(ObjectCategories,UnitCategories) -self.ScanData={} -self.ScanData.Coalitions={} -self.ScanData.Scenery={} -self.ScanData.Units={} -local ZoneCoord=self:GetCoordinate() -local ZoneRadius=self:GetRadius() -self:F({ZoneCoord=ZoneCoord,ZoneRadius=ZoneRadius,ZoneCoordLL=ZoneCoord:ToStringLLDMS()}) -local SphereSearch={ -id=world.VolumeType.SPHERE, -params={ -point=ZoneCoord:GetVec3(), -radius=ZoneRadius, -} -} -local function EvaluateZone(ZoneObject) -if ZoneObject then -local ObjectCategory=ZoneObject:getCategory() -if(ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()and ZoneObject:isActive())or(ObjectCategory==Object.Category.STATIC and ZoneObject:isExist())then -local CoalitionDCSUnit=ZoneObject:getCoalition() -local Include=false -if not UnitCategories then -Include=true -else -local CategoryDCSUnit=ZoneObject:getDesc().category -for UnitCategoryID,UnitCategory in pairs(UnitCategories)do -if UnitCategory==CategoryDCSUnit then -Include=true -break -end -end -end -if Include then -local CoalitionDCSUnit=ZoneObject:getCoalition() -self.ScanData.Coalitions[CoalitionDCSUnit]=true -self.ScanData.Units[ZoneObject]=ZoneObject -self:F2({Name=ZoneObject:getName(),Coalition=CoalitionDCSUnit}) -end -end -if ObjectCategory==Object.Category.SCENERY then -local SceneryType=ZoneObject:getTypeName() -local SceneryName=ZoneObject:getName() -self.ScanData.Scenery[SceneryType]=self.ScanData.Scenery[SceneryType]or{} -self.ScanData.Scenery[SceneryType][SceneryName]=SCENERY:Register(SceneryName,ZoneObject) -self:F2({SCENERY=self.ScanData.Scenery[SceneryType][SceneryName]}) -end -end -return true -end -world.searchObjects(ObjectCategories,SphereSearch,EvaluateZone) -end -function ZONE_RADIUS:GetScannedUnits() -return self.ScanData.Units -end -function ZONE_RADIUS:GetScannedSetUnit() -local SetUnit=SET_UNIT:New() -if self.ScanData then -for ObjectID,UnitObject in pairs(self.ScanData.Units)do -local UnitObject=UnitObject -if UnitObject:isExist()then -local FoundUnit=UNIT:FindByName(UnitObject:getName()) -if FoundUnit then -SetUnit:AddUnit(FoundUnit) -else -local FoundStatic=STATIC:FindByName(UnitObject:getName()) -if FoundStatic then -SetUnit:AddUnit(FoundStatic) -end -end -end -end -end -return SetUnit -end -function ZONE_RADIUS:GetScannedSetGroup() -self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New() -self.ScanSetGroup.Set={} -if self.ScanData then -for ObjectID,UnitObject in pairs(self.ScanData.Units)do -local UnitObject=UnitObject -if UnitObject:isExist()then -local FoundUnit=UNIT:FindByName(UnitObject:getName()) -if FoundUnit then -local group=FoundUnit:GetGroup() -self.ScanSetGroup:AddGroup(group) -end -end -end -end -return self.ScanSetGroup -end -function ZONE_RADIUS:CountScannedCoalitions() -local Count=0 -for CoalitionID,Coalition in pairs(self.ScanData.Coalitions)do -Count=Count+1 -end -return Count -end -function ZONE_RADIUS:CheckScannedCoalition(Coalition) -if Coalition then -return self.ScanData.Coalitions[Coalition] -end -return nil -end -function ZONE_RADIUS:GetScannedCoalition(Coalition) -if Coalition then -return self.ScanData.Coalitions[Coalition] -else -local Count=0 -local ReturnCoalition=nil -for CoalitionID,Coalition in pairs(self.ScanData.Coalitions)do -Count=Count+1 -ReturnCoalition=CoalitionID -end -if Count~=1 then -ReturnCoalition=nil -end -return ReturnCoalition -end -end -function ZONE_RADIUS:GetScannedSceneryType(SceneryType) -return self.ScanData.Scenery[SceneryType] -end -function ZONE_RADIUS:GetScannedScenery() -return self.ScanData.Scenery -end -function ZONE_RADIUS:IsAllInZoneOfCoalition(Coalition) -return self:CountScannedCoalitions()==1 and self:GetScannedCoalition(Coalition)==true -end -function ZONE_RADIUS:IsAllInZoneOfOtherCoalition(Coalition) -return self:CountScannedCoalitions()==1 and self:GetScannedCoalition(Coalition)==nil -end -function ZONE_RADIUS:IsSomeInZoneOfCoalition(Coalition) -return self:CountScannedCoalitions()>1 and self:GetScannedCoalition(Coalition)==true -end -function ZONE_RADIUS:IsNoneInZoneOfCoalition(Coalition) -return self:GetScannedCoalition(Coalition)==nil -end -function ZONE_RADIUS:IsNoneInZone() -return self:CountScannedCoalitions()==0 -end -function ZONE_RADIUS:SearchZone(EvaluateFunction,ObjectCategories) -local SearchZoneResult=true -local ZoneCoord=self:GetCoordinate() -local ZoneRadius=self:GetRadius() -self:F({ZoneCoord=ZoneCoord,ZoneRadius=ZoneRadius,ZoneCoordLL=ZoneCoord:ToStringLLDMS()}) -local SphereSearch={ -id=world.VolumeType.SPHERE, -params={ -point=ZoneCoord:GetVec3(), -radius=ZoneRadius/2, -} -} -local function EvaluateZone(ZoneDCSUnit) -local ZoneUnit=UNIT:Find(ZoneDCSUnit) -return EvaluateFunction(ZoneUnit) -end -world.searchObjects(Object.Category.UNIT,SphereSearch,EvaluateZone) -end -function ZONE_RADIUS:IsVec2InZone(Vec2) -self:F2(Vec2) -local ZoneVec2=self:GetVec2() -if ZoneVec2 then -if((Vec2.x-ZoneVec2.x)^2+(Vec2.y-ZoneVec2.y)^2)^0.5<=self:GetRadius()then -return true -end -end -return false -end -function ZONE_RADIUS:IsVec3InZone(Vec3) -self:F2(Vec3) -local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z}) -return InZone -end -function ZONE_RADIUS:GetRandomVec2(inner,outer) -self:F(self.ZoneName,inner,outer) -local Point={} -local Vec2=self:GetVec2() -local _inner=inner or 0 -local _outer=outer or self:GetRadius() -local angle=math.random()*math.pi*2; -Point.x=Vec2.x+math.cos(angle)*math.random(_inner,_outer); -Point.y=Vec2.y+math.sin(angle)*math.random(_inner,_outer); -self:T({Point}) -return Point -end -function ZONE_RADIUS:GetRandomPointVec2(inner,outer) -self:F(self.ZoneName,inner,outer) -local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2(inner,outer)) -self:T3({PointVec2}) -return PointVec2 -end -function ZONE_RADIUS:GetRandomVec3(inner,outer) -self:F(self.ZoneName,inner,outer) -local Vec2=self:GetRandomVec2(inner,outer) -self:T3({x=Vec2.x,y=self.y,z=Vec2.y}) -return{x=Vec2.x,y=self.y,z=Vec2.y} -end -function ZONE_RADIUS:GetRandomPointVec3(inner,outer) -self:F(self.ZoneName,inner,outer) -local PointVec3=POINT_VEC3:NewFromVec2(self:GetRandomVec2(inner,outer)) -self:T3({PointVec3}) -return PointVec3 -end -function ZONE_RADIUS:GetRandomCoordinate(inner,outer) -self:F(self.ZoneName,inner,outer) -local Coordinate=COORDINATE:NewFromVec2(self:GetRandomVec2(inner,outer)) -self:T3({Coordinate=Coordinate}) -return Coordinate -end -ZONE={ -ClassName="ZONE", -} -function ZONE:New(ZoneName) -local Zone=trigger.misc.getZone(ZoneName) -if not Zone then -error("Zone "..ZoneName.." does not exist.") -return nil -end -local self=BASE:Inherit(self,ZONE_RADIUS:New(ZoneName,{x=Zone.point.x,y=Zone.point.z},Zone.radius)) -self:F(ZoneName) -self.Zone=Zone -return self -end -function ZONE:FindByName(ZoneName) -local ZoneFound=_DATABASE:FindZone(ZoneName) -return ZoneFound -end -ZONE_UNIT={ -ClassName="ZONE_UNIT", -} -function ZONE_UNIT:New(ZoneName,ZoneUNIT,Radius,Offset) -if Offset then -if(Offset.dx or Offset.dy)and(Offset.rho or Offset.theta)then -error("Cannot use (dx, dy) with (rho, theta)") -end -self.dy=Offset.dy or 0.0 -self.dx=Offset.dx or 0.0 -self.rho=Offset.rho or 0.0 -self.theta=(Offset.theta or 0.0)*math.pi/180.0 -self.relative_to_unit=Offset.relative_to_unit or false -end -local self=BASE:Inherit(self,ZONE_RADIUS:New(ZoneName,ZoneUNIT:GetVec2(),Radius)) -self:F({ZoneName,ZoneUNIT:GetVec2(),Radius}) -self.ZoneUNIT=ZoneUNIT -self.LastVec2=ZoneUNIT:GetVec2() -_EVENTDISPATCHER:CreateEventNewZone(self) -return self -end -function ZONE_UNIT:GetVec2() -self:F2(self.ZoneName) -local ZoneVec2=self.ZoneUNIT:GetVec2() -if ZoneVec2 then -local heading -if self.relative_to_unit then -heading=(self.ZoneUNIT:GetHeading()or 0.0)*math.pi/180.0 -else -heading=0.0 -end -if(self.dx or self.dy)then -ZoneVec2.x=ZoneVec2.x+self.dx*math.cos(-heading)+self.dy*math.sin(-heading) -ZoneVec2.y=ZoneVec2.y-self.dx*math.sin(-heading)+self.dy*math.cos(-heading) -end -if(self.rho or self.theta)then -ZoneVec2.x=ZoneVec2.x+self.rho*math.cos(self.theta+heading) -ZoneVec2.y=ZoneVec2.y+self.rho*math.sin(self.theta+heading) -end -self.LastVec2=ZoneVec2 -return ZoneVec2 -else -return self.LastVec2 -end -self:T2({ZoneVec2}) -return nil -end -function ZONE_UNIT:GetRandomVec2() -self:F(self.ZoneName) -local RandomVec2={} -local Vec2=self:GetVec2() -if not Vec2 then -Vec2=self.LastVec2 -end -local angle=math.random()*math.pi*2; -RandomVec2.x=Vec2.x+math.cos(angle)*math.random()*self:GetRadius(); -RandomVec2.y=Vec2.y+math.sin(angle)*math.random()*self:GetRadius(); -self:T({RandomVec2}) -return RandomVec2 -end -function ZONE_UNIT:GetVec3(Height) -self:F2(self.ZoneName) -Height=Height or 0 -local Vec2=self:GetVec2() -local Vec3={x=Vec2.x,y=land.getHeight(self:GetVec2())+Height,z=Vec2.y} -self:T2({Vec3}) -return Vec3 -end -ZONE_GROUP={ -ClassName="ZONE_GROUP", -} -function ZONE_GROUP:New(ZoneName,ZoneGROUP,Radius) -local self=BASE:Inherit(self,ZONE_RADIUS:New(ZoneName,ZoneGROUP:GetVec2(),Radius)) -self:F({ZoneName,ZoneGROUP:GetVec2(),Radius}) -self._.ZoneGROUP=ZoneGROUP -self._.ZoneVec2Cache=self._.ZoneGROUP:GetVec2() -_EVENTDISPATCHER:CreateEventNewZone(self) -return self -end -function ZONE_GROUP:GetVec2() -self:F(self.ZoneName) -local ZoneVec2=nil -if self._.ZoneGROUP:IsAlive()then -ZoneVec2=self._.ZoneGROUP:GetVec2() -self._.ZoneVec2Cache=ZoneVec2 -else -ZoneVec2=self._.ZoneVec2Cache -end -self:T({ZoneVec2}) -return ZoneVec2 -end -function ZONE_GROUP:GetRandomVec2() -self:F(self.ZoneName) -local Point={} -local Vec2=self._.ZoneGROUP:GetVec2() -local angle=math.random()*math.pi*2; -Point.x=Vec2.x+math.cos(angle)*math.random()*self:GetRadius(); -Point.y=Vec2.y+math.sin(angle)*math.random()*self:GetRadius(); -self:T({Point}) -return Point -end -function ZONE_GROUP:GetRandomPointVec2(inner,outer) -self:F(self.ZoneName,inner,outer) -local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2()) -self:T3({PointVec2}) -return PointVec2 -end -ZONE_POLYGON_BASE={ -ClassName="ZONE_POLYGON_BASE", -} -function ZONE_POLYGON_BASE:New(ZoneName,PointsArray) -local self=BASE:Inherit(self,ZONE_BASE:New(ZoneName)) -self:F({ZoneName,PointsArray}) -if PointsArray then -self._.Polygon={} -for i=1,#PointsArray do -self._.Polygon[i]={} -self._.Polygon[i].x=PointsArray[i].x -self._.Polygon[i].y=PointsArray[i].y -end -end -return self -end -function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array) -self._.Polygon={} -for i=1,#Vec2Array do -self._.Polygon[i]={} -self._.Polygon[i].x=Vec2Array[i].x -self._.Polygon[i].y=Vec2Array[i].y -end -return self -end -function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array) -self._.Polygon={} -for i=1,#Vec3Array do -self._.Polygon[i]={} -self._.Polygon[i].x=Vec3Array[i].x -self._.Polygon[i].y=Vec3Array[i].z -end -return self -end -function ZONE_POLYGON_BASE:GetVec2() -self:F(self.ZoneName) -local Bounds=self:GetBoundingSquare() -return{x=(Bounds.x2+Bounds.x1)/2,y=(Bounds.y2+Bounds.y1)/2} -end -function ZONE_POLYGON_BASE:Flush() -self:F2() -self:F({Polygon=self.ZoneName,Coordinates=self._.Polygon}) -return self -end -function ZONE_POLYGON_BASE:BoundZone(UnBound) -local i -local j -local Segments=10 -i=1 -j=#self._.Polygon -while i<=#self._.Polygon do -self:T({i,j,self._.Polygon[i],self._.Polygon[j]}) -local DeltaX=self._.Polygon[j].x-self._.Polygon[i].x -local DeltaY=self._.Polygon[j].y-self._.Polygon[i].y -for Segment=0,Segments do -local PointX=self._.Polygon[i].x+(Segment*DeltaX/Segments) -local PointY=self._.Polygon[i].y+(Segment*DeltaY/Segments) -local Tire={ -["country"]="USA", -["category"]="Fortifications", -["canCargo"]=false, -["shape_name"]="H-tyre_B_WF", -["type"]="Black_Tyre_WF", -["y"]=PointY, -["x"]=PointX, -["name"]=string.format("%s-Tire #%0d",self:GetName(),((i-1)*Segments)+Segment), -["heading"]=0, -} -local Group=coalition.addStaticObject(country.id.USA,Tire) -if UnBound and UnBound==true then -Group:destroy() -end -end -j=i -i=i+1 -end -return self -end -function ZONE_POLYGON_BASE:SmokeZone(SmokeColor,Segments) -self:F2(SmokeColor) -Segments=Segments or 10 -local i=1 -local j=#self._.Polygon -while i<=#self._.Polygon do -self:T({i,j,self._.Polygon[i],self._.Polygon[j]}) -local DeltaX=self._.Polygon[j].x-self._.Polygon[i].x -local DeltaY=self._.Polygon[j].y-self._.Polygon[i].y -for Segment=0,Segments do -local PointX=self._.Polygon[i].x+(Segment*DeltaX/Segments) -local PointY=self._.Polygon[i].y+(Segment*DeltaY/Segments) -POINT_VEC2:New(PointX,PointY):Smoke(SmokeColor) -end -j=i -i=i+1 -end -return self -end -function ZONE_POLYGON_BASE:FlareZone(FlareColor,Segments,Azimuth,AddHeight) -self:F2(FlareColor) -Segments=Segments or 10 -AddHeight=AddHeight or 0 -local i=1 -local j=#self._.Polygon -while i<=#self._.Polygon do -self:T({i,j,self._.Polygon[i],self._.Polygon[j]}) -local DeltaX=self._.Polygon[j].x-self._.Polygon[i].x -local DeltaY=self._.Polygon[j].y-self._.Polygon[i].y -for Segment=0,Segments do -local PointX=self._.Polygon[i].x+(Segment*DeltaX/Segments) -local PointY=self._.Polygon[i].y+(Segment*DeltaY/Segments) -POINT_VEC2:New(PointX,PointY,AddHeight):Flare(FlareColor,Azimuth) -end -j=i -i=i+1 -end -return self -end -function ZONE_POLYGON_BASE:IsVec2InZone(Vec2) -self:F2(Vec2) -local Next -local Prev -local InPolygon=false -Next=1 -Prev=#self._.Polygon -while Next<=#self._.Polygon do -self:T({Next,Prev,self._.Polygon[Next],self._.Polygon[Prev]}) -if(((self._.Polygon[Next].y>Vec2.y)~=(self._.Polygon[Prev].y>Vec2.y))and -(Vec2.x<(self._.Polygon[Prev].x-self._.Polygon[Next].x)*(Vec2.y-self._.Polygon[Next].y)/(self._.Polygon[Prev].y-self._.Polygon[Next].y)+self._.Polygon[Next].x) -)then -InPolygon=not InPolygon -end -self:T2({InPolygon=InPolygon}) -Prev=Next -Next=Next+1 -end -self:T({InPolygon=InPolygon}) -return InPolygon -end -function ZONE_POLYGON_BASE:GetRandomVec2() -self:F2() -local Vec2Found=false -local Vec2 -local BS=self:GetBoundingSquare() -self:T2(BS) -while Vec2Found==false do -Vec2={x=math.random(BS.x1,BS.x2),y=math.random(BS.y1,BS.y2)} -self:T2(Vec2) -if self:IsVec2InZone(Vec2)then -Vec2Found=true -end -end -self:T2(Vec2) -return Vec2 -end -function ZONE_POLYGON_BASE:GetRandomPointVec2() -self:F2() -local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2()) -self:T2(PointVec2) -return PointVec2 -end -function ZONE_POLYGON_BASE:GetRandomPointVec3() -self:F2() -local PointVec3=POINT_VEC3:NewFromVec2(self:GetRandomVec2()) -self:T2(PointVec3) -return PointVec3 -end -function ZONE_POLYGON_BASE:GetRandomCoordinate() -self:F2() -local Coordinate=COORDINATE:NewFromVec2(self:GetRandomVec2()) -self:T2(Coordinate) -return Coordinate -end -function ZONE_POLYGON_BASE:GetBoundingSquare() -local x1=self._.Polygon[1].x -local y1=self._.Polygon[1].y -local x2=self._.Polygon[1].x -local y2=self._.Polygon[1].y -for i=2,#self._.Polygon do -self:T2({self._.Polygon[i],x1,y1,x2,y2}) -x1=(x1>self._.Polygon[i].x)and self._.Polygon[i].x or x1 -x2=(x2self._.Polygon[i].y)and self._.Polygon[i].y or y1 -y2=(y20))then -for group_num,Template in pairs(obj_type_data.group)do -if obj_type_name~="static"and Template and Template.units and type(Template.units)=='table'then -self:_RegisterGroupTemplate(Template,CoalitionSide,_DATABASECategory[string.lower(CategoryName)],CountryID) -else -self:_RegisterStaticTemplate(Template,CoalitionSide,_DATABASECategory[string.lower(CategoryName)],CountryID) -end -end -end -end -end -end -end -end -end -end -return self -end -function DATABASE:AccountHits(Event) -self:F({Event}) -if Event.IniPlayerName~=nil then -self:T("Hitting Something") -if Event.TgtCategory then -self.HITS[Event.TgtUnitName]=self.HITS[Event.TgtUnitName]or{} -local Hit=self.HITS[Event.TgtUnitName] -Hit.Players=Hit.Players or{} -Hit.Players[Event.IniPlayerName]=true -end -end -if Event.WeaponPlayerName~=nil then -self:T("Hitting Scenery") -if Event.TgtCategory then -if Event.WeaponCoalition then -self.HITS[Event.TgtUnitName]=self.HITS[Event.TgtUnitName]or{} -local Hit=self.HITS[Event.TgtUnitName] -Hit.Players=Hit.Players or{} -Hit.Players[Event.WeaponPlayerName]=true -else -end -end -end -end -function DATABASE:AccountDestroys(Event) -self:F({Event}) -local TargetUnit=nil -local TargetGroup=nil -local TargetUnitName="" -local TargetGroupName="" -local TargetPlayerName="" -local TargetCoalition=nil -local TargetCategory=nil -local TargetType=nil -local TargetUnitCoalition=nil -local TargetUnitCategory=nil -local TargetUnitType=nil -if Event.IniDCSUnit then -TargetUnit=Event.IniUnit -TargetUnitName=Event.IniDCSUnitName -TargetGroup=Event.IniDCSGroup -TargetGroupName=Event.IniDCSGroupName -TargetPlayerName=Event.IniPlayerName -TargetCoalition=Event.IniCoalition -TargetCategory=Event.IniCategory -TargetType=Event.IniTypeName -TargetUnitType=TargetType -self:T({TargetUnitName,TargetGroupName,TargetPlayerName,TargetCoalition,TargetCategory,TargetType}) -end -local Destroyed=false -if self.HITS[Event.IniUnitName]then -self.DESTROYS[Event.IniUnitName]=self.DESTROYS[Event.IniUnitName]or{} -self.DESTROYS[Event.IniUnitName]=true -end -end -do -SET_BASE={ -ClassName="SET_BASE", -Filter={}, -Set={}, -List={}, -Index={}, -Database=nil, -CallScheduler=nil, -TimeInterval=nil, -YieldInterval=nil, -} -function SET_BASE:New(Database) -local self=BASE:Inherit(self,FSM:New()) -self.Database=Database -self:SetStartState("Started") -self:AddTransition("*","Added","*") -self:AddTransition("*","Removed","*") -self.YieldInterval=10 -self.TimeInterval=0.001 -self.Set={} -self.Index={} -self.CallScheduler=SCHEDULER:New(self) -self:SetEventPriority(2) -return self -end -function SET_BASE:Clear() -for Name,Object in pairs(self.Set)do -self:Remove(Name) -end -return self -end -function SET_BASE:_Find(ObjectName) -local ObjectFound=self.Set[ObjectName] -return ObjectFound -end -function SET_BASE:GetSet() -self:F2() -return self.Set or{} -end -function SET_BASE:GetSetNames() -self:F2() -local Names={} -for Name,Object in pairs(self.Set)do -table.insert(Names,Name) -end -return Names -end -function SET_BASE:GetSetObjects() -self:F2() -local Objects={} -for Name,Object in pairs(self.Set)do -table.insert(Objects,Object) -end -return Objects -end -function SET_BASE:Remove(ObjectName,NoTriggerEvent) -self:F2({ObjectName=ObjectName}) -local Object=self.Set[ObjectName] -if Object then -for Index,Key in ipairs(self.Index)do -if Key==ObjectName then -table.remove(self.Index,Index) -self.Set[ObjectName]=nil -break -end -end -if not NoTriggerEvent then -self:Removed(ObjectName,Object) -end -end -end -function SET_BASE:Add(ObjectName,Object) -self:F2({ObjectName=ObjectName,Object=Object}) -if self.Set[ObjectName]then -self:Remove(ObjectName,true) -end -self.Set[ObjectName]=Object -table.insert(self.Index,ObjectName) -self:Added(ObjectName,Object) -end -function SET_BASE:AddObject(Object) -self:F2(Object.ObjectName) -self:T(Object.UnitName) -self:T(Object.ObjectName) -self:Add(Object.ObjectName,Object) -end -function SET_BASE:GetSetUnion(SetB) -local union=SET_BASE:New() -for _,ObjectA in pairs(self.Set)do -union:AddObject(ObjectA) -end -for _,ObjectB in pairs(SetB.Set)do -union:AddObject(ObjectB) -end -return union -end -function SET_BASE:GetSetIntersection(SetB) -local intersection=SET_BASE:New() -local union=self:GetSetUnion(SetB) -for _,Object in pairs(union.Set)do -if self:IsIncludeObject(Object)and SetB:IsIncludeObject(Object)then -intersection:AddObject(intersection) -end -end -return intersection -end -function SET_BASE:GetSetComplement(SetB) -local complement=SET_BASE:New() -local union=self:GetSetUnion(SetA,SetB) -for _,Object in pairs(union.Set)do -if SetA:IsIncludeObject(Object)and SetB:IsIncludeObject(Object)then -intersection:Add(intersection) -end -end -return intersection -end -function SET_BASE:CompareSets(SetA,SetB) -for _,ObjectB in pairs(SetB.Set)do -if SetA:IsIncludeObject(ObjectB)then -SetA:Add(ObjectB) -end -end -return SetA -end -function SET_BASE:Get(ObjectName) -self:F(ObjectName) -local Object=self.Set[ObjectName] -self:T3({ObjectName,Object}) -return Object -end -function SET_BASE:GetFirst() -local ObjectName=self.Index[1] -local FirstObject=self.Set[ObjectName] -self:T3({FirstObject}) -return FirstObject -end -function SET_BASE:GetLast() -local ObjectName=self.Index[#self.Index] -local LastObject=self.Set[ObjectName] -self:T3({LastObject}) -return LastObject -end -function SET_BASE:GetRandom() -local RandomItem=self.Set[self.Index[math.random(#self.Index)]] -self:T3({RandomItem}) -return RandomItem -end -function SET_BASE:Count() -return self.Index and#self.Index or 0 -end -function SET_BASE:SetDatabase(BaseSet) -local OtherFilter=routines.utils.deepCopy(BaseSet.Filter) -self.Filter=OtherFilter -self.Database=BaseSet:GetSet() -return self -end -function SET_BASE:SetIteratorIntervals(YieldInterval,TimeInterval) -self.YieldInterval=YieldInterval -self.TimeInterval=TimeInterval -return self -end -function SET_BASE:SetSomeIteratorLimit(Limit) -self.SomeIteratorLimit=Limit or 1 -return self -end -function SET_BASE:GetSomeIteratorLimit() -return self.SomeIteratorLimit or self:Count() -end -function SET_BASE:FilterOnce() -for ObjectName,Object in pairs(self.Database)do -if self:IsIncludeObject(Object)then -self:Add(ObjectName,Object) -end -end -return self -end -function SET_BASE:_FilterStart() -for ObjectName,Object in pairs(self.Database)do -if self:IsIncludeObject(Object)then -self:Add(ObjectName,Object) -end -end -return self -end -function SET_BASE:FilterDeads() -self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash) -return self -end -function SET_BASE:FilterCrashes() -self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash) -return self -end -function SET_BASE:FilterStop() -self:UnHandleEvent(EVENTS.Birth) -self:UnHandleEvent(EVENTS.Dead) -self:UnHandleEvent(EVENTS.Crash) -return self -end -function SET_BASE:FindNearestObjectFromPointVec2(PointVec2) -self:F2(PointVec2) -local NearestObject=nil -local ClosestDistance=nil -for ObjectID,ObjectData in pairs(self.Set)do -if NearestObject==nil then -NearestObject=ObjectData -ClosestDistance=PointVec2:DistanceFromPointVec2(ObjectData:GetCoordinate()) -else -local Distance=PointVec2:DistanceFromPointVec2(ObjectData:GetCoordinate()) -if Distance=Limit then -break -end -end -return true -end -local co=CoRoutine -local function Schedule() -local status,res=co() -self:T3({status,res}) -if status==false then -error(res) -end -if res==false then -return true -end -return false -end -Schedule() -return self -end -function SET_BASE:IsIncludeObject(Object) -self:F3(Object) -return true -end -function SET_BASE:IsInSet(ObjectName) -self:F3(Object) -return true -end -function SET_BASE:GetObjectNames() -self:F3() -local ObjectNames="" -for ObjectName,Object in pairs(self.Set)do -ObjectNames=ObjectNames..ObjectName..", " -end -return ObjectNames -end -function SET_BASE:Flush(MasterObject) -self:F3() -local ObjectNames="" -for ObjectName,Object in pairs(self.Set)do -ObjectNames=ObjectNames..ObjectName..", " -end -self:F({MasterObject=MasterObject and MasterObject:GetClassNameAndID(),"Objects in Set:",ObjectNames}) -return ObjectNames -end -end -do -SET_GROUP={ -ClassName="SET_GROUP", -Filter={ -Coalitions=nil, -Categories=nil, -Countries=nil, -GroupPrefixes=nil, -}, -FilterMeta={ -Coalitions={ -red=coalition.side.RED, -blue=coalition.side.BLUE, -neutral=coalition.side.NEUTRAL, -}, -Categories={ -plane=Group.Category.AIRPLANE, -helicopter=Group.Category.HELICOPTER, -ground=Group.Category.GROUND, -ship=Group.Category.SHIP, -structure=Group.Category.STRUCTURE, -}, -}, -} -function SET_GROUP:New() -local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.GROUPS)) -self:FilterActive(false) -return self -end -function SET_GROUP:GetAliveSet() -self:F2() -local AliveSet=SET_GROUP:New() -for GroupName,GroupObject in pairs(self.Set)do -local GroupObject=GroupObject -if GroupObject then -if GroupObject:IsAlive()then -AliveSet:Add(GroupName,GroupObject) -end -end -end -return AliveSet.Set or{} -end -function SET_GROUP:GetUnitTypeNames() -self:F2() -local MT={} -local UnitTypes={} -local ReportUnitTypes=REPORT:New() -for GroupID,GroupData in pairs(self:GetSet())do -local Units=GroupData:GetUnits() -for UnitID,UnitData in pairs(Units)do -if UnitData:IsAlive()then -local UnitType=UnitData:GetTypeName() -if not UnitTypes[UnitType]then -UnitTypes[UnitType]=1 -else -UnitTypes[UnitType]=UnitTypes[UnitType]+1 -end -end -end -end -for UnitTypeID,UnitType in pairs(UnitTypes)do -ReportUnitTypes:Add(UnitType.." of "..UnitTypeID) -end -return ReportUnitTypes -end -function SET_GROUP:AddGroup(group) -self:Add(group:GetName(),group) -for UnitID,UnitData in pairs(group:GetUnits())do -UnitData:SetCargoBayWeightLimit() -end -return self -end -function SET_GROUP:AddGroupsByName(AddGroupNames) -local AddGroupNamesArray=(type(AddGroupNames)=="table")and AddGroupNames or{AddGroupNames} -for AddGroupID,AddGroupName in pairs(AddGroupNamesArray)do -self:Add(AddGroupName,GROUP:FindByName(AddGroupName)) -end -return self -end -function SET_GROUP:RemoveGroupsByName(RemoveGroupNames) -local RemoveGroupNamesArray=(type(RemoveGroupNames)=="table")and RemoveGroupNames or{RemoveGroupNames} -for RemoveGroupID,RemoveGroupName in pairs(RemoveGroupNamesArray)do -self:Remove(RemoveGroupName) -end -return self -end -function SET_GROUP:FindGroup(GroupName) -local GroupFound=self.Set[GroupName] -return GroupFound -end -function SET_GROUP:FindNearestGroupFromPointVec2(PointVec2) -self:F2(PointVec2) -local NearestGroup=nil -local ClosestDistance=nil -for ObjectID,ObjectData in pairs(self.Set)do -if NearestGroup==nil then -NearestGroup=ObjectData -ClosestDistance=PointVec2:DistanceFromPointVec2(ObjectData:GetCoordinate()) -else -local Distance=PointVec2:DistanceFromPointVec2(ObjectData:GetCoordinate()) -if DistanceMaxThreatLevelA2G then -MaxThreatLevelA2G=ThreatLevelA2G -MaxThreatText=ThreatText -end -end -self:F({MaxThreatLevelA2G=MaxThreatLevelA2G,MaxThreatText=MaxThreatText}) -return MaxThreatLevelA2G,MaxThreatText -end -function SET_UNIT:GetCoordinate() -local Coordinate=self:GetFirst():GetCoordinate() -local x1=Coordinate.x -local x2=Coordinate.x -local y1=Coordinate.y -local y2=Coordinate.y -local z1=Coordinate.z -local z2=Coordinate.z -local MaxVelocity=0 -local AvgHeading=nil -local MovingCount=0 -for UnitName,UnitData in pairs(self:GetSet())do -local Unit=UnitData -local Coordinate=Unit:GetCoordinate() -x1=(Coordinate.xx2)and Coordinate.x or x2 -y1=(Coordinate.yy2)and Coordinate.y or y2 -z1=(Coordinate.yz2)and Coordinate.z or z2 -local Velocity=Coordinate:GetVelocity() -if Velocity~=0 then -MaxVelocity=(MaxVelocity5 then -HeadingSet=nil -break -end -end -end -end -return HeadingSet -end -function SET_UNIT:HasRadar(RadarType) -self:F2(RadarType) -local RadarCount=0 -for UnitID,UnitData in pairs(self:GetSet())do -local UnitSensorTest=UnitData -local HasSensors -if RadarType then -HasSensors=UnitSensorTest:HasSensors(Unit.SensorType.RADAR,RadarType) -else -HasSensors=UnitSensorTest:HasSensors(Unit.SensorType.RADAR) -end -self:T3(HasSensors) -if HasSensors then -RadarCount=RadarCount+1 -end -end -return RadarCount -end -function SET_UNIT:HasSEAD() -self:F2() -local SEADCount=0 -for UnitID,UnitData in pairs(self:GetSet())do -local UnitSEAD=UnitData -if UnitSEAD:IsAlive()then -local UnitSEADAttributes=UnitSEAD:GetDesc().attributes -local HasSEAD=UnitSEAD:HasSEAD() -self:T3(HasSEAD) -if HasSEAD then -SEADCount=SEADCount+1 -end -end -end -return SEADCount -end -function SET_UNIT:HasGroundUnits() -self:F2() -local GroundUnitCount=0 -for UnitID,UnitData in pairs(self:GetSet())do -local UnitTest=UnitData -if UnitTest:IsGround()then -GroundUnitCount=GroundUnitCount+1 -end -end -return GroundUnitCount -end -function SET_UNIT:HasAirUnits() -self:F2() -local AirUnitCount=0 -for UnitID,UnitData in pairs(self:GetSet())do -local UnitTest=UnitData -if UnitTest:IsAir()then -AirUnitCount=AirUnitCount+1 -end -end -return AirUnitCount -end -function SET_UNIT:HasFriendlyUnits(FriendlyCoalition) -self:F2() -local FriendlyUnitCount=0 -for UnitID,UnitData in pairs(self:GetSet())do -local UnitTest=UnitData -if UnitTest:IsFriendly(FriendlyCoalition)then -FriendlyUnitCount=FriendlyUnitCount+1 -end -end -return FriendlyUnitCount -end -function SET_UNIT:IsIncludeObject(MUnit) -self:F2(MUnit) -local MUnitInclude=false -if MUnit:IsAlive()~=nil then -MUnitInclude=true -if self.Filter.Active~=nil then -local MUnitActive=false -if self.Filter.Active==false or(self.Filter.Active==true and MUnit:IsActive()==true)then -MUnitActive=true -end -MUnitInclude=MUnitInclude and MUnitActive -end -if self.Filter.Coalitions then -local MUnitCoalition=false -for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do -self:F({"Coalition:",MUnit:GetCoalition(),self.FilterMeta.Coalitions[CoalitionName],CoalitionName}) -if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==MUnit:GetCoalition()then -MUnitCoalition=true -end -end -MUnitInclude=MUnitInclude and MUnitCoalition -end -if self.Filter.Categories then -local MUnitCategory=false -for CategoryID,CategoryName in pairs(self.Filter.Categories)do -self:T3({"Category:",MUnit:GetDesc().category,self.FilterMeta.Categories[CategoryName],CategoryName}) -if self.FilterMeta.Categories[CategoryName]and self.FilterMeta.Categories[CategoryName]==MUnit:GetDesc().category then -MUnitCategory=true -end -end -MUnitInclude=MUnitInclude and MUnitCategory -end -if self.Filter.Types then -local MUnitType=false -for TypeID,TypeName in pairs(self.Filter.Types)do -self:T3({"Type:",MUnit:GetTypeName(),TypeName}) -if TypeName==MUnit:GetTypeName()then -MUnitType=true -end -end -MUnitInclude=MUnitInclude and MUnitType -end -if self.Filter.Countries then -local MUnitCountry=false -for CountryID,CountryName in pairs(self.Filter.Countries)do -self:T3({"Country:",MUnit:GetCountry(),CountryName}) -if country.id[CountryName]==MUnit:GetCountry()then -MUnitCountry=true -end -end -MUnitInclude=MUnitInclude and MUnitCountry -end -if self.Filter.UnitPrefixes then -local MUnitPrefix=false -for UnitPrefixId,UnitPrefix in pairs(self.Filter.UnitPrefixes)do -self:T3({"Prefix:",string.find(MUnit:GetName(),UnitPrefix,1),UnitPrefix}) -if string.find(MUnit:GetName(),UnitPrefix,1)then -MUnitPrefix=true -end -end -MUnitInclude=MUnitInclude and MUnitPrefix -end -if self.Filter.RadarTypes then -local MUnitRadar=false -for RadarTypeID,RadarType in pairs(self.Filter.RadarTypes)do -self:T3({"Radar:",RadarType}) -if MUnit:HasSensors(Unit.SensorType.RADAR,RadarType)==true then -if MUnit:GetRadar()==true then -self:T3("RADAR Found") -end -MUnitRadar=true -end -end -MUnitInclude=MUnitInclude and MUnitRadar -end -if self.Filter.SEAD then -local MUnitSEAD=false -if MUnit:HasSEAD()==true then -self:T3("SEAD Found") -MUnitSEAD=true -end -MUnitInclude=MUnitInclude and MUnitSEAD -end -end -self:T2(MUnitInclude) -return MUnitInclude -end -function SET_UNIT:GetTypeNames(Delimiter) -Delimiter=Delimiter or", " -local TypeReport=REPORT:New() -local Types={} -for UnitName,UnitData in pairs(self:GetSet())do -local Unit=UnitData -local UnitTypeName=Unit:GetTypeName() -if not Types[UnitTypeName]then -Types[UnitTypeName]=UnitTypeName -TypeReport:Add(UnitTypeName) -end -end -return TypeReport:Text(Delimiter) -end -function SET_UNIT:SetCargoBayWeightLimit() -local Set=self:GetSet() -for UnitID,UnitData in pairs(Set)do -UnitData:SetCargoBayWeightLimit() -end -end -end -do -SET_STATIC={ -ClassName="SET_STATIC", -Statics={}, -Filter={ -Coalitions=nil, -Categories=nil, -Types=nil, -Countries=nil, -StaticPrefixes=nil, -}, -FilterMeta={ -Coalitions={ -red=coalition.side.RED, -blue=coalition.side.BLUE, -neutral=coalition.side.NEUTRAL, -}, -Categories={ -plane=Unit.Category.AIRPLANE, -helicopter=Unit.Category.HELICOPTER, -ground=Unit.Category.GROUND_STATIC, -ship=Unit.Category.SHIP, -structure=Unit.Category.STRUCTURE, -}, -}, -} -function SET_STATIC:New() -local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.STATICS)) -return self -end -function SET_STATIC:AddStatic(AddStatic) -self:F2(AddStatic:GetName()) -self:Add(AddStatic:GetName(),AddStatic) -return self -end -function SET_STATIC:AddStaticsByName(AddStaticNames) -local AddStaticNamesArray=(type(AddStaticNames)=="table")and AddStaticNames or{AddStaticNames} -self:T(AddStaticNamesArray) -for AddStaticID,AddStaticName in pairs(AddStaticNamesArray)do -self:Add(AddStaticName,STATIC:FindByName(AddStaticName)) -end -return self -end -function SET_STATIC:RemoveStaticsByName(RemoveStaticNames) -local RemoveStaticNamesArray=(type(RemoveStaticNames)=="table")and RemoveStaticNames or{RemoveStaticNames} -for RemoveStaticID,RemoveStaticName in pairs(RemoveStaticNamesArray)do -self:Remove(RemoveStaticName) -end -return self -end -function SET_STATIC:FindStatic(StaticName) -local StaticFound=self.Set[StaticName] -return StaticFound -end -function SET_STATIC:FilterCoalitions(Coalitions) -if not self.Filter.Coalitions then -self.Filter.Coalitions={} -end -if type(Coalitions)~="table"then -Coalitions={Coalitions} -end -for CoalitionID,Coalition in pairs(Coalitions)do -self.Filter.Coalitions[Coalition]=Coalition -end -return self -end -function SET_STATIC:FilterCategories(Categories) -if not self.Filter.Categories then -self.Filter.Categories={} -end -if type(Categories)~="table"then -Categories={Categories} -end -for CategoryID,Category in pairs(Categories)do -self.Filter.Categories[Category]=Category -end -return self -end -function SET_STATIC:FilterTypes(Types) -if not self.Filter.Types then -self.Filter.Types={} -end -if type(Types)~="table"then -Types={Types} -end -for TypeID,Type in pairs(Types)do -self.Filter.Types[Type]=Type -end -return self -end -function SET_STATIC:FilterCountries(Countries) -if not self.Filter.Countries then -self.Filter.Countries={} -end -if type(Countries)~="table"then -Countries={Countries} -end -for CountryID,Country in pairs(Countries)do -self.Filter.Countries[Country]=Country -end -return self -end -function SET_STATIC:FilterPrefixes(Prefixes) -if not self.Filter.StaticPrefixes then -self.Filter.StaticPrefixes={} -end -if type(Prefixes)~="table"then -Prefixes={Prefixes} -end -for PrefixID,Prefix in pairs(Prefixes)do -self.Filter.StaticPrefixes[Prefix]=Prefix -end -return self -end -function SET_STATIC:FilterStart() -if _DATABASE then -self:_FilterStart() -self:HandleEvent(EVENTS.Birth,self._EventOnBirth) -self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash) -self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash) -end -return self -end -function SET_STATIC:CountAlive() -local Set=self:GetSet() -local CountU=0 -for UnitID,UnitData in pairs(Set)do -if UnitData and UnitData:IsAlive()then -CountU=CountU+1 -end -end -return CountU -end -function SET_STATIC:AddInDatabase(Event) -self:F3({Event}) -if Event.IniObjectCategory==Object.Category.STATIC then -if not self.Database[Event.IniDCSUnitName]then -self.Database[Event.IniDCSUnitName]=STATIC:Register(Event.IniDCSUnitName) -self:T3(self.Database[Event.IniDCSUnitName]) -end -end -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_STATIC:FindInDatabase(Event) -self:F2({Event.IniDCSUnitName,self.Set[Event.IniDCSUnitName],Event}) -return Event.IniDCSUnitName,self.Set[Event.IniDCSUnitName] -end -do -function SET_STATIC:IsPartiallyInZone(Zone) -local IsPartiallyInZone=false -local function EvaluateZone(ZoneStatic) -local ZoneStaticName=ZoneStatic:GetName() -if self:FindStatic(ZoneStaticName)then -IsPartiallyInZone=true -return false -end -return true -end -return IsPartiallyInZone -end -function SET_STATIC:IsNotInZone(Zone) -local IsNotInZone=true -local function EvaluateZone(ZoneStatic) -local ZoneStaticName=ZoneStatic:GetName() -if self:FindStatic(ZoneStaticName)then -IsNotInZone=false -return false -end -return true -end -Zone:Search(EvaluateZone) -return IsNotInZone -end -function SET_STATIC:ForEachStaticInZone(IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet()) -return self -end -end -function SET_STATIC:ForEachStatic(IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet()) -return self -end -function SET_STATIC:ForEachStaticCompletelyInZone(ZoneObject,IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet(), -function(ZoneObject,StaticObject) -if StaticObject:IsInZone(ZoneObject)then -return true -else -return false -end -end,{ZoneObject}) -return self -end -function SET_STATIC:ForEachStaticNotInZone(ZoneObject,IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet(), -function(ZoneObject,StaticObject) -if StaticObject:IsNotInZone(ZoneObject)then -return true -else -return false -end -end,{ZoneObject}) -return self -end -function SET_STATIC:GetStaticTypes() -self:F2() -local MT={} -local StaticTypes={} -for StaticID,StaticData in pairs(self:GetSet())do -local TextStatic=StaticData -if TextStatic:IsAlive()then -local StaticType=TextStatic:GetTypeName() -if not StaticTypes[StaticType]then -StaticTypes[StaticType]=1 -else -StaticTypes[StaticType]=StaticTypes[StaticType]+1 -end -end -end -for StaticTypeID,StaticType in pairs(StaticTypes)do -MT[#MT+1]=StaticType.." of "..StaticTypeID -end -return StaticTypes -end -function SET_STATIC:GetStaticTypesText() -self:F2() -local MT={} -local StaticTypes=self:GetStaticTypes() -for StaticTypeID,StaticType in pairs(StaticTypes)do -MT[#MT+1]=StaticType.." of "..StaticTypeID -end -return table.concat(MT,", ") -end -function SET_STATIC:GetCoordinate() -local Coordinate=self:GetFirst():GetCoordinate() -local x1=Coordinate.x -local x2=Coordinate.x -local y1=Coordinate.y -local y2=Coordinate.y -local z1=Coordinate.z -local z2=Coordinate.z -local MaxVelocity=0 -local AvgHeading=nil -local MovingCount=0 -for StaticName,StaticData in pairs(self:GetSet())do -local Static=StaticData -local Coordinate=Static:GetCoordinate() -x1=(Coordinate.xx2)and Coordinate.x or x2 -y1=(Coordinate.yy2)and Coordinate.y or y2 -z1=(Coordinate.yz2)and Coordinate.z or z2 -local Velocity=Coordinate:GetVelocity() -if Velocity~=0 then -MaxVelocity=(MaxVelocity5 then -HeadingSet=nil -break -end -end -end -end -return HeadingSet -end -function SET_STATIC:CalculateThreatLevelA2G() -local MaxThreatLevelA2G=0 -local MaxThreatText="" -for StaticName,StaticData in pairs(self:GetSet())do -local ThreatStatic=StaticData -local ThreatLevelA2G,ThreatText=ThreatStatic:GetThreatLevel() -if ThreatLevelA2G>MaxThreatLevelA2G then -MaxThreatLevelA2G=ThreatLevelA2G -MaxThreatText=ThreatText -end -end -self:F({MaxThreatLevelA2G=MaxThreatLevelA2G,MaxThreatText=MaxThreatText}) -return MaxThreatLevelA2G,MaxThreatText -end -function SET_STATIC:IsIncludeObject(MStatic) -self:F2(MStatic) -local MStaticInclude=true -if self.Filter.Coalitions then -local MStaticCoalition=false -for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do -self:T3({"Coalition:",MStatic:GetCoalition(),self.FilterMeta.Coalitions[CoalitionName],CoalitionName}) -if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==MStatic:GetCoalition()then -MStaticCoalition=true -end -end -MStaticInclude=MStaticInclude and MStaticCoalition -end -if self.Filter.Categories then -local MStaticCategory=false -for CategoryID,CategoryName in pairs(self.Filter.Categories)do -self:T3({"Category:",MStatic:GetDesc().category,self.FilterMeta.Categories[CategoryName],CategoryName}) -if self.FilterMeta.Categories[CategoryName]and self.FilterMeta.Categories[CategoryName]==MStatic:GetDesc().category then -MStaticCategory=true -end -end -MStaticInclude=MStaticInclude and MStaticCategory -end -if self.Filter.Types then -local MStaticType=false -for TypeID,TypeName in pairs(self.Filter.Types)do -self:T3({"Type:",MStatic:GetTypeName(),TypeName}) -if TypeName==MStatic:GetTypeName()then -MStaticType=true -end -end -MStaticInclude=MStaticInclude and MStaticType -end -if self.Filter.Countries then -local MStaticCountry=false -for CountryID,CountryName in pairs(self.Filter.Countries)do -self:T3({"Country:",MStatic:GetCountry(),CountryName}) -if country.id[CountryName]==MStatic:GetCountry()then -MStaticCountry=true -end -end -MStaticInclude=MStaticInclude and MStaticCountry -end -if self.Filter.StaticPrefixes then -local MStaticPrefix=false -for StaticPrefixId,StaticPrefix in pairs(self.Filter.StaticPrefixes)do -self:T3({"Prefix:",string.find(MStatic:GetName(),StaticPrefix,1),StaticPrefix}) -if string.find(MStatic:GetName(),StaticPrefix,1)then -MStaticPrefix=true -end -end -MStaticInclude=MStaticInclude and MStaticPrefix -end -self:T2(MStaticInclude) -return MStaticInclude -end -function SET_STATIC:GetTypeNames(Delimiter) -Delimiter=Delimiter or", " -local TypeReport=REPORT:New() -local Types={} -for StaticName,StaticData in pairs(self:GetSet())do -local Static=StaticData -local StaticTypeName=Static:GetTypeName() -if not Types[StaticTypeName]then -Types[StaticTypeName]=StaticTypeName -TypeReport:Add(StaticTypeName) -end -end -return TypeReport:Text(Delimiter) -end -end -do -SET_CLIENT={ -ClassName="SET_CLIENT", -Clients={}, -Filter={ -Coalitions=nil, -Categories=nil, -Types=nil, -Countries=nil, -ClientPrefixes=nil, -}, -FilterMeta={ -Coalitions={ -red=coalition.side.RED, -blue=coalition.side.BLUE, -neutral=coalition.side.NEUTRAL, -}, -Categories={ -plane=Unit.Category.AIRPLANE, -helicopter=Unit.Category.HELICOPTER, -ground=Unit.Category.GROUND_UNIT, -ship=Unit.Category.SHIP, -structure=Unit.Category.STRUCTURE, -}, -}, -} -function SET_CLIENT:New() -local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.CLIENTS)) -self:FilterActive(false) -return self -end -function SET_CLIENT:AddClientsByName(AddClientNames) -local AddClientNamesArray=(type(AddClientNames)=="table")and AddClientNames or{AddClientNames} -for AddClientID,AddClientName in pairs(AddClientNamesArray)do -self:Add(AddClientName,CLIENT:FindByName(AddClientName)) -end -return self -end -function SET_CLIENT:RemoveClientsByName(RemoveClientNames) -local RemoveClientNamesArray=(type(RemoveClientNames)=="table")and RemoveClientNames or{RemoveClientNames} -for RemoveClientID,RemoveClientName in pairs(RemoveClientNamesArray)do -self:Remove(RemoveClientName.ClientName) -end -return self -end -function SET_CLIENT:FindClient(ClientName) -local ClientFound=self.Set[ClientName] -return ClientFound -end -function SET_CLIENT:FilterCoalitions(Coalitions) -if not self.Filter.Coalitions then -self.Filter.Coalitions={} -end -if type(Coalitions)~="table"then -Coalitions={Coalitions} -end -for CoalitionID,Coalition in pairs(Coalitions)do -self.Filter.Coalitions[Coalition]=Coalition -end -return self -end -function SET_CLIENT:FilterCategories(Categories) -if not self.Filter.Categories then -self.Filter.Categories={} -end -if type(Categories)~="table"then -Categories={Categories} -end -for CategoryID,Category in pairs(Categories)do -self.Filter.Categories[Category]=Category -end -return self -end -function SET_CLIENT:FilterTypes(Types) -if not self.Filter.Types then -self.Filter.Types={} -end -if type(Types)~="table"then -Types={Types} -end -for TypeID,Type in pairs(Types)do -self.Filter.Types[Type]=Type -end -return self -end -function SET_CLIENT:FilterCountries(Countries) -if not self.Filter.Countries then -self.Filter.Countries={} -end -if type(Countries)~="table"then -Countries={Countries} -end -for CountryID,Country in pairs(Countries)do -self.Filter.Countries[Country]=Country -end -return self -end -function SET_CLIENT:FilterPrefixes(Prefixes) -if not self.Filter.ClientPrefixes then -self.Filter.ClientPrefixes={} -end -if type(Prefixes)~="table"then -Prefixes={Prefixes} -end -for PrefixID,Prefix in pairs(Prefixes)do -self.Filter.ClientPrefixes[Prefix]=Prefix -end -return self -end -function SET_CLIENT:FilterActive(Active) -Active=Active or not(Active==false) -self.Filter.Active=Active -return self -end -function SET_CLIENT:FilterStart() -if _DATABASE then -self:_FilterStart() -self:HandleEvent(EVENTS.Birth,self._EventOnBirth) -self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash) -self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash) -end -return self -end -function SET_CLIENT:AddInDatabase(Event) -self:F3({Event}) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_CLIENT:FindInDatabase(Event) -self:F3({Event}) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_CLIENT:ForEachClient(IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet()) -return self -end -function SET_CLIENT:ForEachClientInZone(ZoneObject,IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet(), -function(ZoneObject,ClientObject) -if ClientObject:IsInZone(ZoneObject)then -return true -else -return false -end -end,{ZoneObject}) -return self -end -function SET_CLIENT:ForEachClientNotInZone(ZoneObject,IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet(), -function(ZoneObject,ClientObject) -if ClientObject:IsNotInZone(ZoneObject)then -return true -else -return false -end -end,{ZoneObject}) -return self -end -function SET_CLIENT:IsIncludeObject(MClient) -self:F2(MClient) -local MClientInclude=true -if MClient then -local MClientName=MClient.UnitName -if self.Filter.Active~=nil then -local MClientActive=false -if self.Filter.Active==false or(self.Filter.Active==true and MClient:IsActive()==true)then -MClientActive=true -end -MClientInclude=MClientInclude and MClientActive -end -if self.Filter.Coalitions then -local MClientCoalition=false -for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do -local ClientCoalitionID=_DATABASE:GetCoalitionFromClientTemplate(MClientName) -self:T3({"Coalition:",ClientCoalitionID,self.FilterMeta.Coalitions[CoalitionName],CoalitionName}) -if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==ClientCoalitionID then -MClientCoalition=true -end -end -self:T({"Evaluated Coalition",MClientCoalition}) -MClientInclude=MClientInclude and MClientCoalition -end -if self.Filter.Categories then -local MClientCategory=false -for CategoryID,CategoryName in pairs(self.Filter.Categories)do -local ClientCategoryID=_DATABASE:GetCategoryFromClientTemplate(MClientName) -self:T3({"Category:",ClientCategoryID,self.FilterMeta.Categories[CategoryName],CategoryName}) -if self.FilterMeta.Categories[CategoryName]and self.FilterMeta.Categories[CategoryName]==ClientCategoryID then -MClientCategory=true -end -end -self:T({"Evaluated Category",MClientCategory}) -MClientInclude=MClientInclude and MClientCategory -end -if self.Filter.Types then -local MClientType=false -for TypeID,TypeName in pairs(self.Filter.Types)do -self:T3({"Type:",MClient:GetTypeName(),TypeName}) -if TypeName==MClient:GetTypeName()then -MClientType=true -end -end -self:T({"Evaluated Type",MClientType}) -MClientInclude=MClientInclude and MClientType -end -if self.Filter.Countries then -local MClientCountry=false -for CountryID,CountryName in pairs(self.Filter.Countries)do -local ClientCountryID=_DATABASE:GetCountryFromClientTemplate(MClientName) -self:T3({"Country:",ClientCountryID,country.id[CountryName],CountryName}) -if country.id[CountryName]and country.id[CountryName]==ClientCountryID then -MClientCountry=true -end -end -self:T({"Evaluated Country",MClientCountry}) -MClientInclude=MClientInclude and MClientCountry -end -if self.Filter.ClientPrefixes then -local MClientPrefix=false -for ClientPrefixId,ClientPrefix in pairs(self.Filter.ClientPrefixes)do -self:T3({"Prefix:",string.find(MClient.UnitName,ClientPrefix,1),ClientPrefix}) -if string.find(MClient.UnitName,ClientPrefix,1)then -MClientPrefix=true -end -end -self:T({"Evaluated Prefix",MClientPrefix}) -MClientInclude=MClientInclude and MClientPrefix -end -end -self:T2(MClientInclude) -return MClientInclude -end -end -do -SET_PLAYER={ -ClassName="SET_PLAYER", -Clients={}, -Filter={ -Coalitions=nil, -Categories=nil, -Types=nil, -Countries=nil, -ClientPrefixes=nil, -}, -FilterMeta={ -Coalitions={ -red=coalition.side.RED, -blue=coalition.side.BLUE, -neutral=coalition.side.NEUTRAL, -}, -Categories={ -plane=Unit.Category.AIRPLANE, -helicopter=Unit.Category.HELICOPTER, -ground=Unit.Category.GROUND_UNIT, -ship=Unit.Category.SHIP, -structure=Unit.Category.STRUCTURE, -}, -}, -} -function SET_PLAYER:New() -local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.PLAYERS)) -return self -end -function SET_PLAYER:AddClientsByName(AddClientNames) -local AddClientNamesArray=(type(AddClientNames)=="table")and AddClientNames or{AddClientNames} -for AddClientID,AddClientName in pairs(AddClientNamesArray)do -self:Add(AddClientName,CLIENT:FindByName(AddClientName)) -end -return self -end -function SET_PLAYER:RemoveClientsByName(RemoveClientNames) -local RemoveClientNamesArray=(type(RemoveClientNames)=="table")and RemoveClientNames or{RemoveClientNames} -for RemoveClientID,RemoveClientName in pairs(RemoveClientNamesArray)do -self:Remove(RemoveClientName.ClientName) -end -return self -end -function SET_PLAYER:FindClient(PlayerName) -local ClientFound=self.Set[PlayerName] -return ClientFound -end -function SET_PLAYER:FilterCoalitions(Coalitions) -if not self.Filter.Coalitions then -self.Filter.Coalitions={} -end -if type(Coalitions)~="table"then -Coalitions={Coalitions} -end -for CoalitionID,Coalition in pairs(Coalitions)do -self.Filter.Coalitions[Coalition]=Coalition -end -return self -end -function SET_PLAYER:FilterCategories(Categories) -if not self.Filter.Categories then -self.Filter.Categories={} -end -if type(Categories)~="table"then -Categories={Categories} -end -for CategoryID,Category in pairs(Categories)do -self.Filter.Categories[Category]=Category -end -return self -end -function SET_PLAYER:FilterTypes(Types) -if not self.Filter.Types then -self.Filter.Types={} -end -if type(Types)~="table"then -Types={Types} -end -for TypeID,Type in pairs(Types)do -self.Filter.Types[Type]=Type -end -return self -end -function SET_PLAYER:FilterCountries(Countries) -if not self.Filter.Countries then -self.Filter.Countries={} -end -if type(Countries)~="table"then -Countries={Countries} -end -for CountryID,Country in pairs(Countries)do -self.Filter.Countries[Country]=Country -end -return self -end -function SET_PLAYER:FilterPrefixes(Prefixes) -if not self.Filter.ClientPrefixes then -self.Filter.ClientPrefixes={} -end -if type(Prefixes)~="table"then -Prefixes={Prefixes} -end -for PrefixID,Prefix in pairs(Prefixes)do -self.Filter.ClientPrefixes[Prefix]=Prefix -end -return self -end -function SET_PLAYER:FilterStart() -if _DATABASE then -self:_FilterStart() -self:HandleEvent(EVENTS.Birth,self._EventOnBirth) -self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash) -self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash) -end -return self -end -function SET_PLAYER:AddInDatabase(Event) -self:F3({Event}) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_PLAYER:FindInDatabase(Event) -self:F3({Event}) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_PLAYER:ForEachPlayer(IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet()) -return self -end -function SET_PLAYER:ForEachPlayerInZone(ZoneObject,IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet(), -function(ZoneObject,ClientObject) -if ClientObject:IsInZone(ZoneObject)then -return true -else -return false -end -end,{ZoneObject}) -return self -end -function SET_PLAYER:ForEachPlayerNotInZone(ZoneObject,IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet(), -function(ZoneObject,ClientObject) -if ClientObject:IsNotInZone(ZoneObject)then -return true -else -return false -end -end,{ZoneObject}) -return self -end -function SET_PLAYER:IsIncludeObject(MClient) -self:F2(MClient) -local MClientInclude=true -if MClient then -local MClientName=MClient.UnitName -if self.Filter.Coalitions then -local MClientCoalition=false -for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do -local ClientCoalitionID=_DATABASE:GetCoalitionFromClientTemplate(MClientName) -self:T3({"Coalition:",ClientCoalitionID,self.FilterMeta.Coalitions[CoalitionName],CoalitionName}) -if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==ClientCoalitionID then -MClientCoalition=true -end -end -self:T({"Evaluated Coalition",MClientCoalition}) -MClientInclude=MClientInclude and MClientCoalition -end -if self.Filter.Categories then -local MClientCategory=false -for CategoryID,CategoryName in pairs(self.Filter.Categories)do -local ClientCategoryID=_DATABASE:GetCategoryFromClientTemplate(MClientName) -self:T3({"Category:",ClientCategoryID,self.FilterMeta.Categories[CategoryName],CategoryName}) -if self.FilterMeta.Categories[CategoryName]and self.FilterMeta.Categories[CategoryName]==ClientCategoryID then -MClientCategory=true -end -end -self:T({"Evaluated Category",MClientCategory}) -MClientInclude=MClientInclude and MClientCategory -end -if self.Filter.Types then -local MClientType=false -for TypeID,TypeName in pairs(self.Filter.Types)do -self:T3({"Type:",MClient:GetTypeName(),TypeName}) -if TypeName==MClient:GetTypeName()then -MClientType=true -end -end -self:T({"Evaluated Type",MClientType}) -MClientInclude=MClientInclude and MClientType -end -if self.Filter.Countries then -local MClientCountry=false -for CountryID,CountryName in pairs(self.Filter.Countries)do -local ClientCountryID=_DATABASE:GetCountryFromClientTemplate(MClientName) -self:T3({"Country:",ClientCountryID,country.id[CountryName],CountryName}) -if country.id[CountryName]and country.id[CountryName]==ClientCountryID then -MClientCountry=true -end -end -self:T({"Evaluated Country",MClientCountry}) -MClientInclude=MClientInclude and MClientCountry -end -if self.Filter.ClientPrefixes then -local MClientPrefix=false -for ClientPrefixId,ClientPrefix in pairs(self.Filter.ClientPrefixes)do -self:T3({"Prefix:",string.find(MClient.UnitName,ClientPrefix,1),ClientPrefix}) -if string.find(MClient.UnitName,ClientPrefix,1)then -MClientPrefix=true -end -end -self:T({"Evaluated Prefix",MClientPrefix}) -MClientInclude=MClientInclude and MClientPrefix -end -end -self:T2(MClientInclude) -return MClientInclude -end -end -do -SET_AIRBASE={ -ClassName="SET_AIRBASE", -Airbases={}, -Filter={ -Coalitions=nil, -}, -FilterMeta={ -Coalitions={ -red=coalition.side.RED, -blue=coalition.side.BLUE, -neutral=coalition.side.NEUTRAL, -}, -Categories={ -airdrome=Airbase.Category.AIRDROME, -helipad=Airbase.Category.HELIPAD, -ship=Airbase.Category.SHIP, -}, -}, -} -function SET_AIRBASE:New() -local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.AIRBASES)) -return self -end -function SET_AIRBASE:AddAirbase(airbase) -self:Add(airbase:GetName(),airbase) -return self -end -function SET_AIRBASE:AddAirbasesByName(AddAirbaseNames) -local AddAirbaseNamesArray=(type(AddAirbaseNames)=="table")and AddAirbaseNames or{AddAirbaseNames} -for AddAirbaseID,AddAirbaseName in pairs(AddAirbaseNamesArray)do -self:Add(AddAirbaseName,AIRBASE:FindByName(AddAirbaseName)) -end -return self -end -function SET_AIRBASE:RemoveAirbasesByName(RemoveAirbaseNames) -local RemoveAirbaseNamesArray=(type(RemoveAirbaseNames)=="table")and RemoveAirbaseNames or{RemoveAirbaseNames} -for RemoveAirbaseID,RemoveAirbaseName in pairs(RemoveAirbaseNamesArray)do -self:Remove(RemoveAirbaseName) -end -return self -end -function SET_AIRBASE:FindAirbase(AirbaseName) -local AirbaseFound=self.Set[AirbaseName] -return AirbaseFound -end -function SET_AIRBASE:FindAirbaseInRange(Coordinate,Range) -local AirbaseFound=nil -for AirbaseName,AirbaseObject in pairs(self.Set)do -local AirbaseCoordinate=AirbaseObject:GetCoordinate() -local Distance=Coordinate:Get2DDistance(AirbaseCoordinate) -self:F({Distance=Distance}) -if Distance<=Range then -AirbaseFound=AirbaseObject -break -end -end -return AirbaseFound -end -function SET_AIRBASE:GetRandomAirbase() -local RandomAirbase=self:GetRandom() -self:F({RandomAirbase=RandomAirbase:GetName()}) -return RandomAirbase -end -function SET_AIRBASE:FilterCoalitions(Coalitions) -if not self.Filter.Coalitions then -self.Filter.Coalitions={} -end -if type(Coalitions)~="table"then -Coalitions={Coalitions} -end -for CoalitionID,Coalition in pairs(Coalitions)do -self.Filter.Coalitions[Coalition]=Coalition -end -return self -end -function SET_AIRBASE:FilterCategories(Categories) -if not self.Filter.Categories then -self.Filter.Categories={} -end -if type(Categories)~="table"then -Categories={Categories} -end -for CategoryID,Category in pairs(Categories)do -self.Filter.Categories[Category]=Category -end -return self -end -function SET_AIRBASE:FilterStart() -if _DATABASE then -self:HandleEvent(EVENTS.BaseCaptured) -self:HandleEvent(EVENTS.Dead) -for ObjectName,Object in pairs(self.Database)do -if self:IsIncludeObject(Object)then -self:Add(ObjectName,Object) -else -self:RemoveAirbasesByName(ObjectName) -end -end -end -return self -end -function SET_AIRBASE:OnEventBaseCaptured(EventData) -for ObjectName,Object in pairs(self.Database)do -if self:IsIncludeObject(Object)then -self:Add(ObjectName,Object) -else -self:RemoveAirbasesByName(ObjectName) -end -end -end -function SET_AIRBASE:OnEventDead(EventData) -local airbaseName,airbase=self:FindInDatabase(EventData) -if airbase and airbase:IsShip()or airbase:IsHelipad()then -self:RemoveAirbasesByName(airbaseName) -end -end -function SET_AIRBASE:AddInDatabase(Event) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_AIRBASE:FindInDatabase(Event) -self:F3({Event}) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_AIRBASE:ForEachAirbase(IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet()) -return self -end -function SET_AIRBASE:FindNearestAirbaseFromPointVec2(PointVec2) -self:F2(PointVec2) -local NearestAirbase=self:FindNearestObjectFromPointVec2(PointVec2) -return NearestAirbase -end -function SET_AIRBASE:IsIncludeObject(MAirbase) -self:F2(MAirbase) -local MAirbaseInclude=true -if MAirbase then -local MAirbaseName=MAirbase:GetName() -if self.Filter.Coalitions then -local MAirbaseCoalition=false -for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do -local AirbaseCoalitionID=_DATABASE:GetCoalitionFromAirbase(MAirbaseName) -self:T3({"Coalition:",AirbaseCoalitionID,self.FilterMeta.Coalitions[CoalitionName],CoalitionName}) -if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==AirbaseCoalitionID then -MAirbaseCoalition=true -end -end -self:T({"Evaluated Coalition",MAirbaseCoalition}) -MAirbaseInclude=MAirbaseInclude and MAirbaseCoalition -end -if self.Filter.Categories then -local MAirbaseCategory=false -for CategoryID,CategoryName in pairs(self.Filter.Categories)do -local AirbaseCategoryID=_DATABASE:GetCategoryFromAirbase(MAirbaseName) -self:T3({"Category:",AirbaseCategoryID,self.FilterMeta.Categories[CategoryName],CategoryName}) -if self.FilterMeta.Categories[CategoryName]and self.FilterMeta.Categories[CategoryName]==AirbaseCategoryID then -MAirbaseCategory=true -end -end -self:T({"Evaluated Category",MAirbaseCategory}) -MAirbaseInclude=MAirbaseInclude and MAirbaseCategory -end -end -self:T2(MAirbaseInclude) -return MAirbaseInclude -end -end -do -SET_CARGO={ -ClassName="SET_CARGO", -Cargos={}, -Filter={ -Coalitions=nil, -Types=nil, -Countries=nil, -ClientPrefixes=nil, -}, -FilterMeta={ -Coalitions={ -red=coalition.side.RED, -blue=coalition.side.BLUE, -neutral=coalition.side.NEUTRAL, -}, -}, -} -function SET_CARGO:New() -local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.CARGOS)) -return self -end -function SET_CARGO:AddCargo(Cargo) -self:Add(Cargo:GetName(),Cargo) -return self -end -function SET_CARGO:AddCargosByName(AddCargoNames) -local AddCargoNamesArray=(type(AddCargoNames)=="table")and AddCargoNames or{AddCargoNames} -for AddCargoID,AddCargoName in pairs(AddCargoNamesArray)do -self:Add(AddCargoName,CARGO:FindByName(AddCargoName)) -end -return self -end -function SET_CARGO:RemoveCargosByName(RemoveCargoNames) -local RemoveCargoNamesArray=(type(RemoveCargoNames)=="table")and RemoveCargoNames or{RemoveCargoNames} -for RemoveCargoID,RemoveCargoName in pairs(RemoveCargoNamesArray)do -self:Remove(RemoveCargoName.CargoName) -end -return self -end -function SET_CARGO:FindCargo(CargoName) -local CargoFound=self.Set[CargoName] -return CargoFound -end -function SET_CARGO:FilterCoalitions(Coalitions) -if not self.Filter.Coalitions then -self.Filter.Coalitions={} -end -if type(Coalitions)~="table"then -Coalitions={Coalitions} -end -for CoalitionID,Coalition in pairs(Coalitions)do -self.Filter.Coalitions[Coalition]=Coalition -end -return self -end -function SET_CARGO:FilterTypes(Types) -if not self.Filter.Types then -self.Filter.Types={} -end -if type(Types)~="table"then -Types={Types} -end -for TypeID,Type in pairs(Types)do -self.Filter.Types[Type]=Type -end -return self -end -function SET_CARGO:FilterCountries(Countries) -if not self.Filter.Countries then -self.Filter.Countries={} -end -if type(Countries)~="table"then -Countries={Countries} -end -for CountryID,Country in pairs(Countries)do -self.Filter.Countries[Country]=Country -end -return self -end -function SET_CARGO:FilterPrefixes(Prefixes) -if not self.Filter.CargoPrefixes then -self.Filter.CargoPrefixes={} -end -if type(Prefixes)~="table"then -Prefixes={Prefixes} -end -for PrefixID,Prefix in pairs(Prefixes)do -self.Filter.CargoPrefixes[Prefix]=Prefix -end -return self -end -function SET_CARGO:FilterStart() -if _DATABASE then -self:_FilterStart() -self:HandleEvent(EVENTS.NewCargo) -self:HandleEvent(EVENTS.DeleteCargo) -end -return self -end -function SET_CARGO:FilterStop() -self:UnHandleEvent(EVENTS.NewCargo) -self:UnHandleEvent(EVENTS.DeleteCargo) -return self -end -function SET_CARGO:AddInDatabase(Event) -self:F3({Event}) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_CARGO:FindInDatabase(Event) -self:F3({Event}) -return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName] -end -function SET_CARGO:ForEachCargo(IteratorFunction,...) -self:F2(arg) -self:ForEach(IteratorFunction,arg,self:GetSet()) -return self -end -function SET_CARGO:FindNearestCargoFromPointVec2(PointVec2) -self:F2(PointVec2) -local NearestCargo=self:FindNearestObjectFromPointVec2(PointVec2) -return NearestCargo -end -function SET_CARGO:FirstCargoWithState(State) -local FirstCargo=nil -for CargoName,Cargo in pairs(self.Set)do -if Cargo:Is(State)then -FirstCargo=Cargo -break -end -end -return FirstCargo -end -function SET_CARGO:FirstCargoWithStateAndNotDeployed(State) -local FirstCargo=nil -for CargoName,Cargo in pairs(self.Set)do -if Cargo:Is(State)and not Cargo:IsDeployed()then -FirstCargo=Cargo -break -end -end -return FirstCargo -end -function SET_CARGO:FirstCargoUnLoaded() -local FirstCargo=self:FirstCargoWithState("UnLoaded") -return FirstCargo -end -function SET_CARGO:FirstCargoUnLoadedAndNotDeployed() -local FirstCargo=self:FirstCargoWithStateAndNotDeployed("UnLoaded") -return FirstCargo -end -function SET_CARGO:FirstCargoLoaded() -local FirstCargo=self:FirstCargoWithState("Loaded") -return FirstCargo -end -function SET_CARGO:FirstCargoDeployed() -local FirstCargo=self:FirstCargoWithState("Deployed") -return FirstCargo -end -function SET_CARGO:IsIncludeObject(MCargo) -self:F2(MCargo) -local MCargoInclude=true -if MCargo then -local MCargoName=MCargo:GetName() -if self.Filter.Coalitions then -local MCargoCoalition=false -for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do -local CargoCoalitionID=MCargo:GetCoalition() -self:T3({"Coalition:",CargoCoalitionID,self.FilterMeta.Coalitions[CoalitionName],CoalitionName}) -if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==CargoCoalitionID then -MCargoCoalition=true -end -end -self:F({"Evaluated Coalition",MCargoCoalition}) -MCargoInclude=MCargoInclude and MCargoCoalition -end -if self.Filter.Types then -local MCargoType=false -for TypeID,TypeName in pairs(self.Filter.Types)do -self:T3({"Type:",MCargo:GetType(),TypeName}) -if TypeName==MCargo:GetType()then -MCargoType=true -end -end -self:F({"Evaluated Type",MCargoType}) -MCargoInclude=MCargoInclude and MCargoType -end -if self.Filter.CargoPrefixes then -local MCargoPrefix=false -for CargoPrefixId,CargoPrefix in pairs(self.Filter.CargoPrefixes)do -self:T3({"Prefix:",string.find(MCargo.Name,CargoPrefix,1),CargoPrefix}) -if string.find(MCargo.Name,CargoPrefix,1)then -MCargoPrefix=true -end -end -self:F({"Evaluated Prefix",MCargoPrefix}) -MCargoInclude=MCargoInclude and MCargoPrefix -end -end -self:T2(MCargoInclude) -return MCargoInclude -end -function SET_CARGO:OnEventNewCargo(EventData) -self:F({"New Cargo",EventData}) -if EventData.Cargo then -if EventData.Cargo and self:IsIncludeObject(EventData.Cargo)then -self:Add(EventData.Cargo.Name,EventData.Cargo) -end -end -end -function SET_CARGO:OnEventDeleteCargo(EventData) -self:F3({EventData}) -if EventData.Cargo then -local Cargo=_DATABASE:FindCargo(EventData.Cargo.Name) -if Cargo and Cargo.Name then -self:F({CargoNoDestroy=Cargo.NoDestroy}) -if Cargo.NoDestroy then -else -self:Remove(Cargo.Name) -end -end -end -end -end -do -SET_ZONE={ -ClassName="SET_ZONE", -Zones={}, -Filter={ -Prefixes=nil, -}, -FilterMeta={ -}, -} -function SET_ZONE:New() -local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.ZONES)) -return self -end -function SET_ZONE:AddZonesByName(AddZoneNames) -local AddZoneNamesArray=(type(AddZoneNames)=="table")and AddZoneNames or{AddZoneNames} -for AddAirbaseID,AddZoneName in pairs(AddZoneNamesArray)do -self:Add(AddZoneName,ZONE:FindByName(AddZoneName)) -end -return self -end -function SET_ZONE:AddZone(Zone) -self:Add(Zone:GetName(),Zone) -return self -end -function SET_ZONE:RemoveZonesByName(RemoveZoneNames) -local RemoveZoneNamesArray=(type(RemoveZoneNames)=="table")and RemoveZoneNames or{RemoveZoneNames} -for RemoveZoneID,RemoveZoneName in pairs(RemoveZoneNamesArray)do -self:Remove(RemoveZoneName) -end -return self -end -function SET_ZONE:FindZone(ZoneName) -local ZoneFound=self.Set[ZoneName] -return ZoneFound -end -function SET_ZONE:GetRandomZone(margin) -local margin=margin or 100 -if self:Count()~=0 then -local Index=self.Index -local ZoneFound=nil -local counter=0 -while(not ZoneFound)or(counter=self.x and z-Precision<=self.z and z+Precision>=self.z -end -function COORDINATE:ScanObjects(radius,scanunits,scanstatics,scanscenery) -self:F(string.format("Scanning in radius %.1f m.",radius or 100)) -local SphereSearch={ -id=world.VolumeType.SPHERE, -params={ -point=self:GetVec3(), -radius=radius, -} -} -radius=radius or 100 -if scanunits==nil then -scanunits=true -end -if scanstatics==nil then -scanstatics=true -end -if scanscenery==nil then -scanscenery=false -end -local scanobjects={} -if scanunits then -table.insert(scanobjects,Object.Category.UNIT) -end -if scanstatics then -table.insert(scanobjects,Object.Category.STATIC) -end -if scanscenery then -table.insert(scanobjects,Object.Category.SCENERY) -end -local Units={} -local Statics={} -local Scenery={} -local gotstatics=false -local gotunits=false -local gotscenery=false -local function EvaluateZone(ZoneObject) -if ZoneObject then -local ObjectCategory=ZoneObject:getCategory() -if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()then -table.insert(Units,UNIT:Find(ZoneObject)) -gotunits=true -elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist()then -table.insert(Statics,ZoneObject) -gotstatics=true -elseif ObjectCategory==Object.Category.SCENERY then -table.insert(Scenery,ZoneObject) -gotscenery=true -end -end -return true -end -world.searchObjects(scanobjects,SphereSearch,EvaluateZone) -for _,unit in pairs(Units)do -self:T(string.format("Scan found unit %s",unit:GetName())) -end -for _,static in pairs(Statics)do -self:T(string.format("Scan found static %s",static:getName())) -_DATABASE:AddStatic(static:getName()) -end -for _,scenery in pairs(Scenery)do -self:T(string.format("Scan found scenery %s typename=%s",scenery:getName(),scenery:getTypeName())) -end -return gotunits,gotstatics,gotscenery,Units,Statics,Scenery -end -function COORDINATE:ScanUnits(radius) -local _,_,_,units=self:ScanObjects(radius,true,false,false) -local set=SET_UNIT:New() -for _,unit in pairs(units)do -set:AddUnit(unit) -end -return set -end -function COORDINATE:FindClosestUnit(radius) -local units=self:ScanUnits(radius) -local umin=nil -local dmin=math.huge -for _,_unit in pairs(units.Set)do -local unit=_unit -local coordinate=unit:GetCoordinate() -local d=self:Get2DDistance(coordinate) -if d1 then -Radials=2-Radials -end -local RadialMultiplier -if InnerRadius and InnerRadius<=OuterRadius then -RadialMultiplier=(OuterRadius-InnerRadius)*Radials+InnerRadius -else -RadialMultiplier=OuterRadius*Radials -end -local RandomVec2 -if OuterRadius>0 then -RandomVec2={x=math.cos(Theta)*RadialMultiplier+self.x,y=math.sin(Theta)*RadialMultiplier+self.z} -else -RandomVec2={x=self.x,y=self.z} -end -return RandomVec2 -end -function COORDINATE:GetRandomCoordinateInRadius(OuterRadius,InnerRadius) -self:F2({OuterRadius,InnerRadius}) -return COORDINATE:NewFromVec2(self:GetRandomVec2InRadius(OuterRadius,InnerRadius)) -end -function COORDINATE:GetRandomVec3InRadius(OuterRadius,InnerRadius) -local RandomVec2=self:GetRandomVec2InRadius(OuterRadius,InnerRadius) -local y=self.y+math.random(InnerRadius,OuterRadius) -local RandomVec3={x=RandomVec2.x,y=y,z=RandomVec2.y} -return RandomVec3 -end -function COORDINATE:GetLandHeight() -local Vec2={x=self.x,y=self.z} -return land.getHeight(Vec2) -end -function COORDINATE:SetHeading(Heading) -self.Heading=Heading -end -function COORDINATE:GetHeading() -return self.Heading -end -function COORDINATE:SetVelocity(Velocity) -self.Velocity=Velocity -end -function COORDINATE:GetVelocity() -local Velocity=self.Velocity -return Velocity or 0 -end -function COORDINATE:GetName() -local name=self:ToStringMGRS() -return name -end -function COORDINATE:GetMovingText(Settings) -return self:GetVelocityText(Settings)..", "..self:GetHeadingText(Settings) -end -function COORDINATE:GetDirectionVec3(TargetCoordinate) -return{x=TargetCoordinate.x-self.x,y=TargetCoordinate.y-self.y,z=TargetCoordinate.z-self.z} -end -function COORDINATE:GetNorthCorrectionRadians() -local TargetVec3=self:GetVec3() -local lat,lon=coord.LOtoLL(TargetVec3) -local north_posit=coord.LLtoLO(lat+1,lon) -return math.atan2(north_posit.z-TargetVec3.z,north_posit.x-TargetVec3.x) -end -function COORDINATE:GetAngleRadians(DirectionVec3) -local DirectionRadians=math.atan2(DirectionVec3.z,DirectionVec3.x) -if DirectionRadians<0 then -DirectionRadians=DirectionRadians+2*math.pi -end -return DirectionRadians -end -function COORDINATE:GetAngleDegrees(DirectionVec3) -local AngleRadians=self:GetAngleRadians(DirectionVec3) -local Angle=UTILS.ToDegree(AngleRadians) -return Angle -end -function COORDINATE:GetIntermediateCoordinate(ToCoordinate,Fraction) -local f=Fraction or 0.5 -local vec=UTILS.VecSubstract(ToCoordinate,self) -vec.x=f*vec.x -vec.y=f*vec.y -vec.z=f*vec.z -vec=UTILS.VecAdd(self,vec) -local coord=COORDINATE:New(vec.x,vec.y,vec.z) -return coord -end -function COORDINATE:Get2DDistance(TargetCoordinate) -local a={x=TargetCoordinate.x-self.x,y=0,z=TargetCoordinate.z-self.z} -local norm=UTILS.VecNorm(a) -return norm -end -function COORDINATE:GetTemperature(height) -self:F2(height) -local y=height or self.y -local point={x=self.x,y=height or self.y,z=self.z} -local T,P=atmosphere.getTemperatureAndPressure(point) -return T-273.15 -end -function COORDINATE:GetTemperatureText(height,Settings) -local DegreesCelcius=self:GetTemperature(height) -local Settings=Settings or _SETTINGS -if DegreesCelcius then -if Settings:IsMetric()then -return string.format(" %-2.2f °C",DegreesCelcius) -else -return string.format(" %-2.2f °F",UTILS.CelciusToFarenheit(DegreesCelcius)) -end -else -return" no temperature" -end -return nil -end -function COORDINATE:GetPressure(height) -local point={x=self.x,y=height or self.y,z=self.z} -local T,P=atmosphere.getTemperatureAndPressure(point) -return P/100 -end -function COORDINATE:GetPressureText(height,Settings) -local Pressure_hPa=self:GetPressure(height) -local Pressure_mmHg=Pressure_hPa*0.7500615613030 -local Pressure_inHg=Pressure_hPa*0.0295299830714 -local Settings=Settings or _SETTINGS -if Pressure_hPa then -if Settings:IsMetric()then -return string.format(" %4.1f hPa (%3.1f mmHg)",Pressure_hPa,Pressure_mmHg) -else -return string.format(" %4.1f hPa (%3.2f inHg)",Pressure_hPa,Pressure_inHg) -end -else -return" no pressure" -end -return nil -end -function COORDINATE:HeadingTo(ToCoordinate) -local dz=ToCoordinate.z-self.z -local dx=ToCoordinate.x-self.x -local heading=math.deg(math.atan2(dz,dx)) -if heading<0 then -heading=360+heading -end -return heading -end -function COORDINATE:GetWind(height) -local landheight=self:GetLandHeight()+0.1 -local point={x=self.x,y=math.max(height or self.y,landheight),z=self.z} -local wind=atmosphere.getWind(point) -local direction=math.deg(math.atan2(wind.z,wind.x)) -if direction<0 then -direction=360+direction -end -if direction>180 then -direction=direction-180 -else -direction=direction+180 -end -local strength=math.sqrt((wind.x)^2+(wind.z)^2) -return direction,strength -end -function COORDINATE:GetWindWithTurbulenceVec3(height) -local landheight=self:GetLandHeight()+0.1 -local point={x=self.x,y=math.max(height or self.y,landheight),z=self.z} -local vec3=atmosphere.getWindWithTurbulence(point) -return vec3 -end -function COORDINATE:GetWindText(height,Settings) -local Direction,Strength=self:GetWind(height) -local Settings=Settings or _SETTINGS -if Direction and Strength then -if Settings:IsMetric()then -return string.format(" %d ° at %3.2f mps",Direction,UTILS.MpsToKmph(Strength)) -else -return string.format(" %d ° at %3.2f kps",Direction,UTILS.MpsToKnots(Strength)) -end -else -return" no wind" -end -return nil -end -function COORDINATE:Get3DDistance(TargetCoordinate) -local TargetVec3=TargetCoordinate:GetVec3() -local SourceVec3=self:GetVec3() -return((TargetVec3.x-SourceVec3.x)^2+(TargetVec3.y-SourceVec3.y)^2+(TargetVec3.z-SourceVec3.z)^2)^0.5 -end -function COORDINATE:GetBearingText(AngleRadians,Precision,Settings,Language) -local Settings=Settings or _SETTINGS -local AngleDegrees=UTILS.Round(UTILS.ToDegree(AngleRadians),Precision) -local s=string.format('%03d°',AngleDegrees) -return s -end -function COORDINATE:GetDistanceText(Distance,Settings,Language) -local Settings=Settings or _SETTINGS -local Language=Language or"EN" -local DistanceText -if Settings:IsMetric()then -if Language=="EN"then -DistanceText=" for "..UTILS.Round(Distance/1000,2).." km" -elseif Language=="RU"then -DistanceText=" за "..UTILS.Round(Distance/1000,2).." километров" -end -else -if Language=="EN"then -DistanceText=" for "..UTILS.Round(UTILS.MetersToNM(Distance),2).." miles" -elseif Language=="RU"then -DistanceText=" за "..UTILS.Round(UTILS.MetersToNM(Distance),2).." миль" -end -end -return DistanceText -end -function COORDINATE:GetAltitudeText(Settings,Language) -local Altitude=self.y -local Settings=Settings or _SETTINGS -local Language=Language or"EN" -if Altitude~=0 then -if Settings:IsMetric()then -if Language=="EN"then -return" at "..UTILS.Round(self.y,-3).." meters" -elseif Language=="RU"then -return" в "..UTILS.Round(self.y,-3).." метры" -end -else -if Language=="EN"then -return" at "..UTILS.Round(UTILS.MetersToFeet(self.y),-3).." feet" -elseif Language=="RU"then -return" в "..UTILS.Round(self.y,-3).." ноги" -end -end -else -return"" -end -end -function COORDINATE:GetVelocityText(Settings) -local Velocity=self:GetVelocity() -local Settings=Settings or _SETTINGS -if Velocity then -if Settings:IsMetric()then -return string.format(" moving at %d km/h",UTILS.MpsToKmph(Velocity)) -else -return string.format(" moving at %d mi/h",UTILS.MpsToKmph(Velocity)/1.852) -end -else -return" stationary" -end -end -function COORDINATE:GetHeadingText(Settings) -local Heading=self:GetHeading() -if Heading then -return string.format(" bearing %3d°",Heading) -else -return" bearing unknown" -end -end -function COORDINATE:GetBRText(AngleRadians,Distance,Settings,Language) -local Settings=Settings or _SETTINGS -local BearingText=self:GetBearingText(AngleRadians,0,Settings,Language) -local DistanceText=self:GetDistanceText(Distance,Settings,Language) -local BRText=BearingText..DistanceText -return BRText -end -function COORDINATE:GetBRAText(AngleRadians,Distance,Settings,Language) -local Settings=Settings or _SETTINGS -local BearingText=self:GetBearingText(AngleRadians,0,Settings,Language) -local DistanceText=self:GetDistanceText(Distance,Settings,Language) -local AltitudeText=self:GetAltitudeText(Settings,Language) -local BRAText=BearingText..DistanceText..AltitudeText -return BRAText -end -function COORDINATE:SetAltitude(altitude,asl) -local alt=altitude -if asl then -alt=altitude -else -alt=self:GetLandHeight()+altitude -end -self.y=alt -return self -end -function COORDINATE:WaypointAir(AltType,Type,Action,Speed,SpeedLocked,airbase,DCSTasks,description,timeReFuAr) -self:F2({AltType,Type,Action,Speed,SpeedLocked}) -AltType=AltType or"RADIO" -if SpeedLocked==nil then -SpeedLocked=true -end -Speed=Speed or 500 -local RoutePoint={} -RoutePoint.x=self.x -RoutePoint.y=self.z -RoutePoint.alt=self.y -RoutePoint.alt_type=AltType -RoutePoint.type=Type or nil -RoutePoint.action=Action or nil -RoutePoint.speed=Speed/3.6 -RoutePoint.speed_locked=SpeedLocked -RoutePoint.ETA=0 -RoutePoint.ETA_locked=true -RoutePoint.name=description -if airbase then -local AirbaseID=airbase:GetID() -local AirbaseCategory=airbase:GetAirbaseCategory() -if AirbaseCategory==Airbase.Category.SHIP or AirbaseCategory==Airbase.Category.HELIPAD then -RoutePoint.linkUnit=AirbaseID -RoutePoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.AIRDROME then -RoutePoint.airdromeId=AirbaseID -else -self:E("ERROR: Unknown airbase category in COORDINATE:WaypointAir()!") -end -end -if Type==COORDINATE.WaypointType.LandingReFuAr then -RoutePoint.timeReFuAr=timeReFuAr or 10 -end -RoutePoint.task={} -RoutePoint.task.id="ComboTask" -RoutePoint.task.params={} -RoutePoint.task.params.tasks=DCSTasks or{} -self:T({RoutePoint=RoutePoint}) -return RoutePoint -end -function COORDINATE:WaypointAirTurningPoint(AltType,Speed,DCSTasks,description) -return self:WaypointAir(AltType,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true,nil,DCSTasks,description) -end -function COORDINATE:WaypointAirFlyOverPoint(AltType,Speed) -return self:WaypointAir(AltType,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.FlyoverPoint,Speed) -end -function COORDINATE:WaypointAirTakeOffParkingHot(AltType,Speed) -return self:WaypointAir(AltType,COORDINATE.WaypointType.TakeOffParkingHot,COORDINATE.WaypointAction.FromParkingAreaHot,Speed) -end -function COORDINATE:WaypointAirTakeOffParking(AltType,Speed) -return self:WaypointAir(AltType,COORDINATE.WaypointType.TakeOffParking,COORDINATE.WaypointAction.FromParkingArea,Speed) -end -function COORDINATE:WaypointAirTakeOffRunway(AltType,Speed) -return self:WaypointAir(AltType,COORDINATE.WaypointType.TakeOff,COORDINATE.WaypointAction.FromRunway,Speed) -end -function COORDINATE:WaypointAirLanding(Speed,airbase,DCSTasks,description) -return self:WaypointAir(nil,COORDINATE.WaypointType.Land,COORDINATE.WaypointAction.Landing,Speed,false,airbase,DCSTasks,description) -end -function COORDINATE:WaypointAirLandingReFu(Speed,airbase,timeReFuAr,DCSTasks,description) -return self:WaypointAir(nil,COORDINATE.WaypointType.LandingReFuAr,COORDINATE.WaypointAction.LandingReFuAr,Speed,false,airbase,DCSTasks,description,timeReFuAr or 10) -end -function COORDINATE:WaypointGround(Speed,Formation,DCSTasks) -self:F2({Speed,Formation,DCSTasks}) -local RoutePoint={} -RoutePoint.x=self.x -RoutePoint.y=self.z -RoutePoint.alt=self:GetLandHeight()+1 -RoutePoint.alt_type=COORDINATE.WaypointAltType.BARO -RoutePoint.type="Turning Point" -RoutePoint.action=Formation or"Off Road" -RoutePoint.formation_template="" -RoutePoint.ETA=0 -RoutePoint.ETA_locked=true -RoutePoint.speed=(Speed or 20)/3.6 -RoutePoint.speed_locked=true -RoutePoint.task={} -RoutePoint.task.id="ComboTask" -RoutePoint.task.params={} -RoutePoint.task.params.tasks=DCSTasks or{} -return RoutePoint -end -function COORDINATE:WaypointNaval(Speed,Depth,DCSTasks) -self:F2({Speed,Depth,DCSTasks}) -local RoutePoint={} -RoutePoint.x=self.x -RoutePoint.y=self.z -RoutePoint.alt=Depth or self.y -RoutePoint.alt_type="BARO" -RoutePoint.type="Turning Point" -RoutePoint.action="Turning Point" -RoutePoint.formation_template="" -RoutePoint.ETA=0 -RoutePoint.ETA_locked=true -RoutePoint.speed=(Speed or 20)/3.6 -RoutePoint.speed_locked=true -RoutePoint.task={} -RoutePoint.task.id="ComboTask" -RoutePoint.task.params={} -RoutePoint.task.params.tasks=DCSTasks or{} -return RoutePoint -end -function COORDINATE:GetClosestAirbase2(Category,Coalition) -local airbases=AIRBASE.GetAllAirbases(Coalition) -local closest=nil -local distmin=nil -for _,_airbase in pairs(airbases)do -local airbase=_airbase -if airbase then -local category=airbase:GetAirbaseCategory() -if Category and Category==category or Category==nil then -local dist=self:Get2DDistance(airbase:GetCoordinate()) -if closest==nil then -distmin=dist -closest=airbase -else -if dist=2 then -for i=1,#Path-1 do -Way=Way+Path[i+1]:Get2DDistance(Path[i]) -end -else -return nil,nil,false -end -return Path,Way,GotPath -end -function COORDINATE:GetSurfaceType() -local vec2=self:GetVec2() -local surface=land.getSurfaceType(vec2) -return surface -end -function COORDINATE:IsSurfaceTypeLand() -return self:GetSurfaceType()==land.SurfaceType.LAND -end -function COORDINATE:IsSurfaceTypeLand() -return self:GetSurfaceType()==land.SurfaceType.LAND -end -function COORDINATE:IsSurfaceTypeRoad() -return self:GetSurfaceType()==land.SurfaceType.ROAD -end -function COORDINATE:IsSurfaceTypeRunway() -return self:GetSurfaceType()==land.SurfaceType.RUNWAY -end -function COORDINATE:IsSurfaceTypeShallowWater() -return self:GetSurfaceType()==land.SurfaceType.SHALLOW_WATER -end -function COORDINATE:IsSurfaceTypeWater() -return self:GetSurfaceType()==land.SurfaceType.WATER -end -function COORDINATE:Explosion(ExplosionIntensity,Delay) -self:F2({ExplosionIntensity}) -ExplosionIntensity=ExplosionIntensity or 100 -if Delay and Delay>0 then -self:ScheduleOnce(Delay,self.Explosion,self,ExplosionIntensity) -else -trigger.action.explosion(self:GetVec3(),ExplosionIntensity) -end -return self -end -function COORDINATE:IlluminationBomb(power) -self:F2() -trigger.action.illuminationBomb(self:GetVec3(),power) -end -function COORDINATE:Smoke(SmokeColor) -self:F2({SmokeColor}) -trigger.action.smoke(self:GetVec3(),SmokeColor) -end -function COORDINATE:SmokeGreen() -self:F2() -self:Smoke(SMOKECOLOR.Green) -end -function COORDINATE:SmokeRed() -self:F2() -self:Smoke(SMOKECOLOR.Red) -end -function COORDINATE:SmokeWhite() -self:F2() -self:Smoke(SMOKECOLOR.White) -end -function COORDINATE:SmokeOrange() -self:F2() -self:Smoke(SMOKECOLOR.Orange) -end -function COORDINATE:SmokeBlue() -self:F2() -self:Smoke(SMOKECOLOR.Blue) -end -function COORDINATE:BigSmokeAndFire(preset,density) -self:F2({preset=preset,density=density}) -density=density or 0.5 -trigger.action.effectSmokeBig(self:GetVec3(),preset,density) -end -function COORDINATE:BigSmokeAndFireSmall(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire,density) -end -function COORDINATE:BigSmokeAndFireMedium(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire,density) -end -function COORDINATE:BigSmokeAndFireLarge(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire,density) -end -function COORDINATE:BigSmokeAndFireHuge(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire,density) -end -function COORDINATE:BigSmokeSmall(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke,density) -end -function COORDINATE:BigSmokeMedium(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke,density) -end -function COORDINATE:BigSmokeLarge(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke,density) -end -function COORDINATE:BigSmokeHuge(density) -self:F2({density=density}) -density=density or 0.5 -self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke,density) -end -function COORDINATE:Flare(FlareColor,Azimuth) -self:F2({FlareColor}) -trigger.action.signalFlare(self:GetVec3(),FlareColor,Azimuth and Azimuth or 0) -end -function COORDINATE:FlareWhite(Azimuth) -self:F2(Azimuth) -self:Flare(FLARECOLOR.White,Azimuth) -end -function COORDINATE:FlareYellow(Azimuth) -self:F2(Azimuth) -self:Flare(FLARECOLOR.Yellow,Azimuth) -end -function COORDINATE:FlareGreen(Azimuth) -self:F2(Azimuth) -self:Flare(FLARECOLOR.Green,Azimuth) -end -function COORDINATE:FlareRed(Azimuth) -self:F2(Azimuth) -self:Flare(FLARECOLOR.Red,Azimuth) -end -do -function COORDINATE:MarkToAll(MarkText,ReadOnly,Text) -local MarkID=UTILS.GetMarkID() -if ReadOnly==nil then -ReadOnly=false -end -local text=Text or"" -trigger.action.markToAll(MarkID,MarkText,self:GetVec3(),ReadOnly,text) -return MarkID -end -function COORDINATE:MarkToCoalition(MarkText,Coalition,ReadOnly,Text) -local MarkID=UTILS.GetMarkID() -if ReadOnly==nil then -ReadOnly=false -end -local text=Text or"" -trigger.action.markToCoalition(MarkID,MarkText,self:GetVec3(),Coalition,ReadOnly,text) -return MarkID -end -function COORDINATE:MarkToCoalitionRed(MarkText,ReadOnly,Text) -return self:MarkToCoalition(MarkText,coalition.side.RED,ReadOnly,Text) -end -function COORDINATE:MarkToCoalitionBlue(MarkText,ReadOnly,Text) -return self:MarkToCoalition(MarkText,coalition.side.BLUE,ReadOnly,Text) -end -function COORDINATE:MarkToGroup(MarkText,MarkGroup,ReadOnly,Text) -local MarkID=UTILS.GetMarkID() -if ReadOnly==nil then -ReadOnly=false -end -local text=Text or"" -trigger.action.markToGroup(MarkID,MarkText,self:GetVec3(),MarkGroup:GetID(),ReadOnly,text) -return MarkID -end -function COORDINATE:RemoveMark(MarkID) -trigger.action.removeMark(MarkID) -end -function COORDINATE:LineToAll(Endpoint,Coalition,LineType,Color,Alpha,ReadOnly,Text) -local MarkID=UTILS.GetMarkID() -if ReadOnly==nil then -ReadOnly=false -end -local vec3=Endpoint:GetVec3() -Coalition=Coalition or-1 -Color=Color or{1,0,0} -Color[4]=Alpha or 1.0 -LineType=LineType or 1 -trigger.action.lineToAll(Coalition,MarkID,self:GetVec3(),vec3,Color,LineType,ReadOnly,Text or"") -return MarkID -end -function COORDINATE:CircleToAll(Radius,Coalition,LineType,Color,Alpha,FillColor,FillAlpha,ReadOnly,Text) -local MarkID=UTILS.GetMarkID() -if ReadOnly==nil then -ReadOnly=false -end -local vec3=self:GetVec3() -Radius=Radius or 1000 -Coalition=Coalition or-1 -Color=Color or{1,0,0} -Color[4]=Alpha or 1.0 -LineType=LineType or 1 -FillColor=FillColor or{1,0,0} -FillColor[4]=FillAlpha or 0.5 -trigger.action.circleToAll(Coalition,MarkID,vec3,Radius,Color,FillColor,LineType,ReadOnly,Text or"") -return MarkID -end -end -function COORDINATE:IsLOS(ToCoordinate,Offset) -Offset=Offset or 2 -local FromVec3=self:GetVec3() -FromVec3.y=FromVec3.y+Offset -local ToVec3=ToCoordinate:GetVec3() -ToVec3.y=ToVec3.y+Offset -local IsLOS=land.isVisible(FromVec3,ToVec3) -return IsLOS -end -function COORDINATE:IsInRadius(Coordinate,Radius) -local InVec2=self:GetVec2() -local Vec2=Coordinate:GetVec2() -local InRadius=UTILS.IsInRadius(InVec2,Vec2,Radius) -return InRadius -end -function COORDINATE:IsInSphere(Coordinate,Radius) -local InVec3=self:GetVec3() -local Vec3=Coordinate:GetVec3() -local InSphere=UTILS.IsInSphere(InVec3,Vec3,Radius) -return InSphere -end -function COORDINATE:GetSunriseAtDate(Day,Month,Year,InSeconds) -local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day) -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff) -if InSeconds then -return sunrise -else -return UTILS.SecondsToClock(sunrise,true) -end -end -function COORDINATE:GetSunriseAtDayOfYear(DayOfYear,InSeconds) -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff) -if InSeconds then -return sunrise -else -return UTILS.SecondsToClock(sunrise,true) -end -end -function COORDINATE:GetSunrise(InSeconds) -local DayOfYear=UTILS.GetMissionDayOfYear() -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff) -local date=UTILS.GetDCSMissionDate() -if InSeconds then -return sunrise -else -return UTILS.SecondsToClock(sunrise,true) -end -end -function COORDINATE:GetMinutesToSunrise(OnlyToday) -local time=UTILS.SecondsOfToday() -local sunrise=nil -local delta=nil -if OnlyToday then -sunrise=self:GetSunrise(true) -delta=sunrise-time -else -local DayOfYear=UTILS.GetMissionDayOfYear()+1 -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff) -delta=sunrise+UTILS.SecondsToMidnight() -end -return delta/60 -end -function COORDINATE:IsDay(Clock) -if Clock then -local Time=UTILS.ClockToSeconds(Clock) -local clock=UTILS.Split(Clock,"+")[1] -local DayOfYear=UTILS.GetMissionDayOfYear(Time) -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff) -local sunset=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff) -local time=UTILS.ClockToSeconds(clock) -if time>sunrise and time<=sunset then -return true -else -return false -end -else -local sunrise=self:GetSunrise(true) -local sunset=self:GetSunset(true) -local time=UTILS.SecondsOfToday() -if time>sunrise and time<=sunset then -return true -else -return false -end -end -end -function COORDINATE:IsNight(Clock) -return not self:IsDay(Clock) -end -function COORDINATE:GetSunsetAtDate(Day,Month,Year,InSeconds) -local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day) -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -local sunset=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff) -if InSeconds then -return sunset -else -return UTILS.SecondsToClock(sunset,true) -end -end -function COORDINATE:GetSunset(InSeconds) -local DayOfYear=UTILS.GetMissionDayOfYear() -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff) -local date=UTILS.GetDCSMissionDate() -if InSeconds then -return sunrise -else -return UTILS.SecondsToClock(sunrise,true) -end -end -function COORDINATE:GetMinutesToSunset(OnlyToday) -local time=UTILS.SecondsOfToday() -local sunset=nil -local delta=nil -if OnlyToday then -sunset=self:GetSunset(true) -delta=sunset-time -else -local DayOfYear=UTILS.GetMissionDayOfYear()+1 -local Latitude,Longitude=self:GetLLDDM() -local Tdiff=UTILS.GMTToLocalTimeDifference() -sunset=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff) -delta=sunset+UTILS.SecondsToMidnight() -end -return delta/60 -end -function COORDINATE:ToStringBR(FromCoordinate,Settings) -local DirectionVec3=FromCoordinate:GetDirectionVec3(self) -local AngleRadians=self:GetAngleRadians(DirectionVec3) -local Distance=self:Get2DDistance(FromCoordinate) -return"BR, "..self:GetBRText(AngleRadians,Distance,Settings) -end -function COORDINATE:ToStringBRA(FromCoordinate,Settings,Language) -local DirectionVec3=FromCoordinate:GetDirectionVec3(self) -local AngleRadians=self:GetAngleRadians(DirectionVec3) -local Distance=FromCoordinate:Get2DDistance(self) -local Altitude=self:GetAltitudeText() -return"BRA, "..self:GetBRAText(AngleRadians,Distance,Settings,Language) -end -function COORDINATE:ToStringBULLS(Coalition,Settings) -local BullsCoordinate=COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition)) -local DirectionVec3=BullsCoordinate:GetDirectionVec3(self) -local AngleRadians=self:GetAngleRadians(DirectionVec3) -local Distance=self:Get2DDistance(BullsCoordinate) -local Altitude=self:GetAltitudeText() -return"BULLS, "..self:GetBRText(AngleRadians,Distance,Settings) -end -function COORDINATE:ToStringAspect(TargetCoordinate) -local Heading=self.Heading -local DirectionVec3=self:GetDirectionVec3(TargetCoordinate) -local Angle=self:GetAngleDegrees(DirectionVec3) -if Heading then -local Aspect=Angle-Heading -if Aspect>-135 and Aspect<=-45 then -return"Flanking" -end -if Aspect>-45 and Aspect<=45 then -return"Hot" -end -if Aspect>45 and Aspect<=135 then -return"Flanking" -end -if Aspect>135 or Aspect<=-135 then -return"Cold" -end -end -return"" -end -function COORDINATE:GetLLDDM() -return coord.LOtoLL(self:GetVec3()) -end -function COORDINATE:ToStringLLDMS(Settings) -local LL_Accuracy=Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy -local lat,lon=coord.LOtoLL(self:GetVec3()) -return"LL DMS "..UTILS.tostringLL(lat,lon,LL_Accuracy,true) -end -function COORDINATE:ToStringLLDDM(Settings) -local LL_Accuracy=Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy -local lat,lon=coord.LOtoLL(self:GetVec3()) -return"LL DDM "..UTILS.tostringLL(lat,lon,LL_Accuracy,false) -end -function COORDINATE:ToStringMGRS(Settings) -local MGRS_Accuracy=Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy -local lat,lon=coord.LOtoLL(self:GetVec3()) -local MGRS=coord.LLtoMGRS(lat,lon) -return"MGRS "..UTILS.tostringMGRS(MGRS,MGRS_Accuracy) -end -function COORDINATE:ToStringFromRP(ReferenceCoord,ReferenceName,Controllable,Settings) -self:F2({ReferenceCoord=ReferenceCoord,ReferenceName=ReferenceName}) -local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS -local IsAir=Controllable and Controllable:IsAirPlane()or false -if IsAir then -local DirectionVec3=ReferenceCoord:GetDirectionVec3(self) -local AngleRadians=self:GetAngleRadians(DirectionVec3) -local Distance=self:Get2DDistance(ReferenceCoord) -return"Targets are the last seen "..self:GetBRText(AngleRadians,Distance,Settings).." from "..ReferenceName -else -local DirectionVec3=ReferenceCoord:GetDirectionVec3(self) -local AngleRadians=self:GetAngleRadians(DirectionVec3) -local Distance=self:Get2DDistance(ReferenceCoord) -return"Target are located "..self:GetBRText(AngleRadians,Distance,Settings).." from "..ReferenceName -end -return nil -end -function COORDINATE:ToStringA2G(Controllable,Settings) -self:F2({Controllable=Controllable and Controllable:GetName()}) -local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS -if Settings:IsA2G_BR()then -if Controllable then -local Coordinate=Controllable:GetCoordinate() -return Controllable and self:ToStringBR(Coordinate,Settings)or self:ToStringMGRS(Settings) -else -return self:ToStringMGRS(Settings) -end -end -if Settings:IsA2G_LL_DMS()then -return self:ToStringLLDMS(Settings) -end -if Settings:IsA2G_LL_DDM()then -return self:ToStringLLDDM(Settings) -end -if Settings:IsA2G_MGRS()then -return self:ToStringMGRS(Settings) -end -return nil -end -function COORDINATE:ToStringA2A(Controllable,Settings,Language) -self:F2({Controllable=Controllable and Controllable:GetName()}) -local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS -if Settings:IsA2A_BRAA()then -if Controllable then -local Coordinate=Controllable:GetCoordinate() -return self:ToStringBRA(Coordinate,Settings,Language) -else -return self:ToStringMGRS(Settings,Language) -end -end -if Settings:IsA2A_BULLS()then -local Coalition=Controllable:GetCoalition() -return self:ToStringBULLS(Coalition,Settings,Language) -end -if Settings:IsA2A_LL_DMS()then -return self:ToStringLLDMS(Settings,Language) -end -if Settings:IsA2A_LL_DDM()then -return self:ToStringLLDDM(Settings,Language) -end -if Settings:IsA2A_MGRS()then -return self:ToStringMGRS(Settings,Language) -end -return nil -end -function COORDINATE:ToString(Controllable,Settings,Task) -local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS -local ModeA2A=nil -if Task then -if Task:IsInstanceOf(TASK_A2A)then -ModeA2A=true -else -if Task:IsInstanceOf(TASK_A2G)then -ModeA2A=false -else -if Task:IsInstanceOf(TASK_CARGO)then -ModeA2A=false -end -if Task:IsInstanceOf(TASK_CAPTURE_ZONE)then -ModeA2A=false -end -end -end -end -if ModeA2A==nil then -local IsAir=Controllable and(Controllable:IsAirPlane()or Controllable:IsHelicopter())or false -if IsAir then -ModeA2A=true -else -ModeA2A=false -end -end -if ModeA2A==true then -return self:ToStringA2A(Controllable,Settings) -else -return self:ToStringA2G(Controllable,Settings) -end -return nil -end -function COORDINATE:ToStringPressure(Controllable,Settings) -self:F2({Controllable=Controllable and Controllable:GetName()}) -local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS -return self:GetPressureText(nil,Settings) -end -function COORDINATE:ToStringWind(Controllable,Settings) -self:F2({Controllable=Controllable and Controllable:GetName()}) -local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS -return self:GetWindText(nil,Settings) -end -function COORDINATE:ToStringTemperature(Controllable,Settings) -self:F2({Controllable=Controllable and Controllable:GetName()}) -local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS -return self:GetTemperatureText(nil,Settings) -end -end -do -POINT_VEC3={ -ClassName="POINT_VEC3", -Metric=true, -RoutePointAltType={ -BARO="BARO", -}, -RoutePointType={ -TakeOffParking="TakeOffParking", -TurningPoint="Turning Point", -}, -RoutePointAction={ -FromParkingArea="From Parking Area", -TurningPoint="Turning Point", -}, -} -function POINT_VEC3:New(x,y,z) -local self=BASE:Inherit(self,COORDINATE:New(x,y,z)) -self:F2(self) -return self -end -function POINT_VEC3:NewFromVec2(Vec2,LandHeightAdd) -local self=BASE:Inherit(self,COORDINATE:NewFromVec2(Vec2,LandHeightAdd)) -self:F2(self) -return self -end -function POINT_VEC3:NewFromVec3(Vec3) -local self=BASE:Inherit(self,COORDINATE:NewFromVec3(Vec3)) -self:F2(self) -return self -end -function POINT_VEC3:GetX() -return self.x -end -function POINT_VEC3:GetY() -return self.y -end -function POINT_VEC3:GetZ() -return self.z -end -function POINT_VEC3:SetX(x) -self.x=x -return self -end -function POINT_VEC3:SetY(y) -self.y=y -return self -end -function POINT_VEC3:SetZ(z) -self.z=z -return self -end -function POINT_VEC3:AddX(x) -self.x=self.x+x -return self -end -function POINT_VEC3:AddY(y) -self.y=self.y+y -return self -end -function POINT_VEC3:AddZ(z) -self.z=self.z+z -return self -end -function POINT_VEC3:GetRandomPointVec3InRadius(OuterRadius,InnerRadius) -return POINT_VEC3:NewFromVec3(self:GetRandomVec3InRadius(OuterRadius,InnerRadius)) -end -end -do -POINT_VEC2={ -ClassName="POINT_VEC2", -} -function POINT_VEC2:New(x,y,LandHeightAdd) -local LandHeight=land.getHeight({["x"]=x,["y"]=y}) -LandHeightAdd=LandHeightAdd or 0 -LandHeight=LandHeight+LandHeightAdd -local self=BASE:Inherit(self,COORDINATE:New(x,LandHeight,y)) -self:F2(self) -return self -end -function POINT_VEC2:NewFromVec2(Vec2,LandHeightAdd) -local LandHeight=land.getHeight(Vec2) -LandHeightAdd=LandHeightAdd or 0 -LandHeight=LandHeight+LandHeightAdd -local self=BASE:Inherit(self,COORDINATE:NewFromVec2(Vec2,LandHeightAdd)) -self:F2(self) -return self -end -function POINT_VEC2:NewFromVec3(Vec3) -local self=BASE:Inherit(self,COORDINATE:NewFromVec3(Vec3)) -self:F2(self) -return self -end -function POINT_VEC2:GetX() -return self.x -end -function POINT_VEC2:GetY() -return self.z -end -function POINT_VEC2:SetX(x) -self.x=x -return self -end -function POINT_VEC2:SetY(y) -self.z=y -return self -end -function POINT_VEC2:GetLat() -return self.x -end -function POINT_VEC2:SetLat(x) -self.x=x -return self -end -function POINT_VEC2:GetLon() -return self.z -end -function POINT_VEC2:SetLon(z) -self.z=z -return self -end -function POINT_VEC2:GetAlt() -return self.y~=0 or land.getHeight({x=self.x,y=self.z}) -end -function POINT_VEC2:SetAlt(Altitude) -self.y=Altitude or land.getHeight({x=self.x,y=self.z}) -return self -end -function POINT_VEC2:AddX(x) -self.x=self.x+x -return self -end -function POINT_VEC2:AddY(y) -self.z=self.z+y -return self -end -function POINT_VEC2:AddAlt(Altitude) -self.y=land.getHeight({x=self.x,y=self.z})+Altitude or 0 -return self -end -function POINT_VEC2:GetRandomPointVec2InRadius(OuterRadius,InnerRadius) -self:F2({OuterRadius,InnerRadius}) -return POINT_VEC2:NewFromVec2(self:GetRandomVec2InRadius(OuterRadius,InnerRadius)) -end -function POINT_VEC2:DistanceFromPointVec2(PointVec2Reference) -self:F2(PointVec2Reference) -local Distance=((PointVec2Reference.x-self.x)^2+(PointVec2Reference.z-self.z)^2)^0.5 -self:T2(Distance) -return Distance -end -end -do -VELOCITY={ -ClassName="VELOCITY", -} -function VELOCITY:New(VelocityMps) -local self=BASE:Inherit(self,BASE:New()) -self:F({}) -self.Velocity=VelocityMps -return self -end -function VELOCITY:Set(VelocityMps) -self.Velocity=VelocityMps -return self -end -function VELOCITY:Get() -return self.Velocity -end -function VELOCITY:SetKmph(VelocityKmph) -self.Velocity=UTILS.KmphToMps(VelocityKmph) -return self -end -function VELOCITY:GetKmph() -return UTILS.MpsToKmph(self.Velocity) -end -function VELOCITY:SetMiph(VelocityMiph) -self.Velocity=UTILS.MiphToMps(VelocityMiph) -return self -end -function VELOCITY:GetMiph() -return UTILS.MpsToMiph(self.Velocity) -end -function VELOCITY:GetText(Settings) -local Settings=Settings or _SETTINGS -if self.Velocity~=0 then -if Settings:IsMetric()then -return string.format("%d km/h",UTILS.MpsToKmph(self.Velocity)) -else -return string.format("%d mi/h",UTILS.MpsToMiph(self.Velocity)) -end -else -return"stationary" -end -end -function VELOCITY:ToString(VelocityGroup,Settings) -self:F({Group=VelocityGroup and VelocityGroup:GetName()}) -local Settings=Settings or(VelocityGroup and _DATABASE:GetPlayerSettings(VelocityGroup:GetPlayerName()))or _SETTINGS -return self:GetText(Settings) -end -end -do -VELOCITY_POSITIONABLE={ -ClassName="VELOCITY_POSITIONABLE", -} -function VELOCITY_POSITIONABLE:New(Positionable) -local self=BASE:Inherit(self,VELOCITY:New()) -self:F({}) -self.Positionable=Positionable -return self -end -function VELOCITY_POSITIONABLE:Get() -return self.Positionable:GetVelocityMPS()or 0 -end -function VELOCITY_POSITIONABLE:GetKmph() -return UTILS.MpsToKmph(self.Positionable:GetVelocityMPS()or 0) -end -function VELOCITY_POSITIONABLE:GetMiph() -return UTILS.MpsToMiph(self.Positionable:GetVelocityMPS()or 0) -end -function VELOCITY_POSITIONABLE:ToString() -self:F({Group=self.Positionable and self.Positionable:GetName()}) -local Settings=Settings or(self.Positionable and _DATABASE:GetPlayerSettings(self.Positionable:GetPlayerName()))or _SETTINGS -self.Velocity=self.Positionable:GetVelocityMPS() -return self:GetText(Settings) -end -end -MESSAGE={ -ClassName="MESSAGE", -MessageCategory=0, -MessageID=0, -} -MESSAGE.Type={ -Update="Update", -Information="Information", -Briefing="Briefing Report", -Overview="Overview Report", -Detailed="Detailed Report" -} -function MESSAGE:New(MessageText,MessageDuration,MessageCategory,ClearScreen) -local self=BASE:Inherit(self,BASE:New()) -self:F({MessageText,MessageDuration,MessageCategory}) -self.MessageType=nil -if MessageCategory and MessageCategory~=""then -if MessageCategory:sub(-1)~="\n"then -self.MessageCategory=MessageCategory..": " -else -self.MessageCategory=MessageCategory:sub(1,-2)..":\n" -end -else -self.MessageCategory="" -end -self.ClearScreen=false -if ClearScreen~=nil then -self.ClearScreen=ClearScreen -end -self.MessageDuration=MessageDuration or 5 -self.MessageTime=timer.getTime() -self.MessageText=MessageText:gsub("^\n","",1):gsub("\n$","",1) -self.MessageSent=false -self.MessageGroup=false -self.MessageCoalition=false -return self -end -function MESSAGE:NewType(MessageText,MessageType,ClearScreen) -local self=BASE:Inherit(self,BASE:New()) -self:F({MessageText}) -self.MessageType=MessageType -self.ClearScreen=false -if ClearScreen~=nil then -self.ClearScreen=ClearScreen -end -self.MessageTime=timer.getTime() -self.MessageText=MessageText:gsub("^\n","",1):gsub("\n$","",1) -return self -end -function MESSAGE:Clear() -self:F() -self.ClearScreen=true -return self -end -function MESSAGE:ToClient(Client,Settings) -self:F(Client) -if Client and Client:GetClientGroupID()then -if self.MessageType then -local Settings=Settings or(Client and _DATABASE:GetPlayerSettings(Client:GetPlayerName()))or _SETTINGS -self.MessageDuration=Settings:GetMessageTime(self.MessageType) -self.MessageCategory="" -end -if self.MessageDuration~=0 then -local ClientGroupID=Client:GetClientGroupID() -self:T(self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$","").." / "..self.MessageDuration) -trigger.action.outTextForGroup(ClientGroupID,self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$",""),self.MessageDuration,self.ClearScreen) -end -end -return self -end -function MESSAGE:ToGroup(Group,Settings) -self:F(Group.GroupName) -if Group then -if self.MessageType then -local Settings=Settings or(Group and _DATABASE:GetPlayerSettings(Group:GetPlayerName()))or _SETTINGS -self.MessageDuration=Settings:GetMessageTime(self.MessageType) -self.MessageCategory="" -end -if self.MessageDuration~=0 then -self:T(self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$","").." / "..self.MessageDuration) -trigger.action.outTextForGroup(Group:GetID(),self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$",""),self.MessageDuration,self.ClearScreen) -end -end -return self -end -function MESSAGE:ToBlue() -self:F() -self:ToCoalition(coalition.side.BLUE) -return self -end -function MESSAGE:ToRed() -self:F() -self:ToCoalition(coalition.side.RED) -return self -end -function MESSAGE:ToCoalition(CoalitionSide,Settings) -self:F(CoalitionSide) -if self.MessageType then -local Settings=Settings or _SETTINGS -self.MessageDuration=Settings:GetMessageTime(self.MessageType) -self.MessageCategory="" -end -if CoalitionSide then -if self.MessageDuration~=0 then -self:T(self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$","").." / "..self.MessageDuration) -trigger.action.outTextForCoalition(CoalitionSide,self.MessageText:gsub("\n$",""):gsub("\n$",""),self.MessageDuration,self.ClearScreen) -end -end -return self -end -function MESSAGE:ToCoalitionIf(CoalitionSide,Condition) -self:F(CoalitionSide) -if Condition and Condition==true then -self:ToCoalition(CoalitionSide) -end -return self -end -function MESSAGE:ToAll(Settings) -self:F() -if self.MessageType then -local Settings=Settings or _SETTINGS -self.MessageDuration=Settings:GetMessageTime(self.MessageType) -self.MessageCategory="" -end -if self.MessageDuration~=0 then -self:T(self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$","").." / "..self.MessageDuration) -trigger.action.outText(self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$",""),self.MessageDuration,self.ClearScreen) -end -return self -end -function MESSAGE:ToAllIf(Condition) -if Condition and Condition==true then -self:ToAll() -end -return self -end -do -FSM={ -ClassName="FSM", -} -function FSM:New() -self=BASE:Inherit(self,BASE:New()) -self.options=options or{} -self.options.subs=self.options.subs or{} -self.current=self.options.initial or'none' -self.Events={} -self.subs={} -self.endstates={} -self.Scores={} -self._StartState="none" -self._Transitions={} -self._Processes={} -self._EndStates={} -self._Scores={} -self._EventSchedules={} -self.CallScheduler=SCHEDULER:New(self) -return self -end -function FSM:SetStartState(State) -self._StartState=State -self.current=State -end -function FSM:GetStartState() -return self._StartState or{} -end -function FSM:AddTransition(From,Event,To) -local Transition={} -Transition.From=From -Transition.Event=Event -Transition.To=To -self:T2(Transition) -self._Transitions[Transition]=Transition -self:_eventmap(self.Events,Transition) -end -function FSM:GetTransitions() -return self._Transitions or{} -end -function FSM:AddProcess(From,Event,Process,ReturnEvents) -self:T({From,Event}) -local Sub={} -Sub.From=From -Sub.Event=Event -Sub.fsm=Process -Sub.StartEvent="Start" -Sub.ReturnEvents=ReturnEvents -self._Processes[Sub]=Sub -self:_submap(self.subs,Sub,nil) -self:AddTransition(From,Event,From) -return Process -end -function FSM:GetProcesses() -self:F({Processes=self._Processes}) -return self._Processes or{} -end -function FSM:GetProcess(From,Event) -for ProcessID,Process in pairs(self:GetProcesses())do -if Process.From==From and Process.Event==Event then -return Process.fsm -end -end -error("Sub-Process from state "..From.." with event "..Event.." not found!") -end -function FSM:SetProcess(From,Event,Fsm) -for ProcessID,Process in pairs(self:GetProcesses())do -if Process.From==From and Process.Event==Event then -Process.fsm=Fsm -return true -end -end -error("Sub-Process from state "..From.." with event "..Event.." not found!") -end -function FSM:AddEndState(State) -self._EndStates[State]=State -self.endstates[State]=State -end -function FSM:GetEndStates() -return self._EndStates or{} -end -function FSM:AddScore(State,ScoreText,Score) -self:F({State,ScoreText,Score}) -self._Scores[State]=self._Scores[State]or{} -self._Scores[State].ScoreText=ScoreText -self._Scores[State].Score=Score -return self -end -function FSM:AddScoreProcess(From,Event,State,ScoreText,Score) -self:F({From,Event,State,ScoreText,Score}) -local Process=self:GetProcess(From,Event) -Process._Scores[State]=Process._Scores[State]or{} -Process._Scores[State].ScoreText=ScoreText -Process._Scores[State].Score=Score -self:T(Process._Scores) -return Process -end -function FSM:GetScores() -return self._Scores or{} -end -function FSM:GetSubs() -return self.options.subs -end -function FSM:LoadCallBacks(CallBackTable) -for name,callback in pairs(CallBackTable or{})do -self[name]=callback -end -end -function FSM:_eventmap(Events,EventStructure) -local Event=EventStructure.Event -local __Event="__"..EventStructure.Event -self[Event]=self[Event]or self:_create_transition(Event) -self[__Event]=self[__Event]or self:_delayed_transition(Event) -self:T2("Added methods: "..Event..", "..__Event) -Events[Event]=self.Events[Event]or{map={}} -self:_add_to_map(Events[Event].map,EventStructure) -end -function FSM:_submap(subs,sub,name) -subs[sub.From]=subs[sub.From]or{} -subs[sub.From][sub.Event]=subs[sub.From][sub.Event]or{} -subs[sub.From][sub.Event][sub]={} -subs[sub.From][sub.Event][sub].fsm=sub.fsm -subs[sub.From][sub.Event][sub].StartEvent=sub.StartEvent -subs[sub.From][sub.Event][sub].ReturnEvents=sub.ReturnEvents or{} -subs[sub.From][sub.Event][sub].name=name -subs[sub.From][sub.Event][sub].fsmparent=self -end -function FSM:_call_handler(step,trigger,params,EventName) -local handler=step..trigger -if self[handler]then -self._EventSchedules[EventName]=nil -local ErrorHandler=function(errmsg) -env.info("Error in SCHEDULER function:"..errmsg) -if BASE.Debug~=nil then -env.info(BASE.Debug.traceback()) -end -return errmsg -end -local Result,Value=xpcall(function()return self[handler](self,unpack(params))end,ErrorHandler) -return Value -end -end -function FSM._handler(self,EventName,...) -local Can,To=self:can(EventName) -if To=="*"then -To=self.current -end -if Can then -local From=self.current -local Params={From,EventName,To,...} -if self["onleave"..From]or -self["OnLeave"..From]or -self["onbefore"..EventName]or -self["OnBefore"..EventName]or -self["onafter"..EventName]or -self["OnAfter"..EventName]or -self["onenter"..To]or -self["OnEnter"..To]then -if self:_call_handler("onbefore",EventName,Params,EventName)==false then -self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** onbefore"..EventName) -return false -else -if self:_call_handler("OnBefore",EventName,Params,EventName)==false then -self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** OnBefore"..EventName) -return false -else -if self:_call_handler("onleave",From,Params,EventName)==false then -self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** onleave"..From) -return false -else -if self:_call_handler("OnLeave",From,Params,EventName)==false then -self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** OnLeave"..From) -return false -end -end -end -end -else -local ClassName=self:GetClassName() -if ClassName=="FSM"then -self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To) -end -if ClassName=="FSM_TASK"then -self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To.." *** Task: "..self.TaskName) -end -if ClassName=="FSM_CONTROLLABLE"then -self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To.." *** TaskUnit: "..self.Controllable.ControllableName.." *** ") -end -if ClassName=="FSM_PROCESS"then -self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To.." *** Task: "..self.Task:GetName()..", TaskUnit: "..self.Controllable.ControllableName.." *** ") -end -end -self.current=To -local execute=true -local subtable=self:_gosub(From,EventName) -for _,sub in pairs(subtable)do -self:T("*** FSM *** Sub *** "..sub.StartEvent) -sub.fsm.fsmparent=self -sub.fsm.ReturnEvents=sub.ReturnEvents -sub.fsm[sub.StartEvent](sub.fsm) -execute=false -end -local fsmparent,Event=self:_isendstate(To) -if fsmparent and Event then -self:T("*** FSM *** End *** "..Event) -self:_call_handler("onenter",To,Params,EventName) -self:_call_handler("OnEnter",To,Params,EventName) -self:_call_handler("onafter",EventName,Params,EventName) -self:_call_handler("OnAfter",EventName,Params,EventName) -self:_call_handler("onstate","change",Params,EventName) -fsmparent[Event](fsmparent) -execute=false -end -if execute then -self:_call_handler("onafter",EventName,Params,EventName) -self:_call_handler("OnAfter",EventName,Params,EventName) -self:_call_handler("onenter",To,Params,EventName) -self:_call_handler("OnEnter",To,Params,EventName) -self:_call_handler("onstate","change",Params,EventName) -end -else -self:T("*** FSM *** NO Transition *** "..self.current.." --> "..EventName.." --> ? ") -end -return nil -end -function FSM:_delayed_transition(EventName) -return function(self,DelaySeconds,...) -self:T2("Delayed Event: "..EventName) -local CallID=0 -if DelaySeconds~=nil then -if DelaySeconds<0 then -DelaySeconds=math.abs(DelaySeconds) -if not self._EventSchedules[EventName]then -CallID=self.CallScheduler:Schedule(self,self._handler,{EventName,...},DelaySeconds or 1,nil,nil,nil,4,true) -self._EventSchedules[EventName]=CallID -self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s",EventName,DelaySeconds,tostring(CallID))) -else -self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec CANCELLED as we already have such an event in the queue.",EventName,DelaySeconds)) -end -else -CallID=self.CallScheduler:Schedule(self,self._handler,{EventName,...},DelaySeconds or 1,nil,nil,nil,4,true) -self:T2(string.format("Event %s delayed by %.1f sec SCHEDULED with CallID=%s",EventName,DelaySeconds,tostring(CallID))) -end -else -error("FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this.") -end -self:T2({CallID=CallID}) -end -end -function FSM:_create_transition(EventName) -return function(self,...)return self._handler(self,EventName,...)end -end -function FSM:_gosub(ParentFrom,ParentEvent) -local fsmtable={} -if self.subs[ParentFrom]and self.subs[ParentFrom][ParentEvent]then -self:T({ParentFrom,ParentEvent,self.subs[ParentFrom],self.subs[ParentFrom][ParentEvent]}) -return self.subs[ParentFrom][ParentEvent] -else -return{} -end -end -function FSM:_isendstate(Current) -local FSMParent=self.fsmparent -if FSMParent and self.endstates[Current]then -FSMParent.current=Current -local ParentFrom=FSMParent.current -local Event=self.ReturnEvents[Current] -if Event then -return FSMParent,Event -else -end -end -return nil -end -function FSM:_add_to_map(Map,Event) -self:F3({Map,Event}) -if type(Event.From)=='string'then -Map[Event.From]=Event.To -else -for _,From in ipairs(Event.From)do -Map[From]=Event.To -end -end -self:T3({Map,Event}) -end -function FSM:GetState() -return self.current -end -function FSM:GetCurrentState() -return self.current -end -function FSM:Is(State) -return self.current==State -end -function FSM:is(state) -return self.current==state -end -function FSM:can(e) -local Event=self.Events[e] -local To=Event and Event.map[self.current]or Event.map['*'] -return To~=nil,To -end -function FSM:cannot(e) -return not self:can(e) -end -end -do -FSM_CONTROLLABLE={ -ClassName="FSM_CONTROLLABLE", -} -function FSM_CONTROLLABLE:New(Controllable) -local self=BASE:Inherit(self,FSM:New()) -if Controllable then -self:SetControllable(Controllable) -end -self:AddTransition("*","Stop","Stopped") -return self -end -function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To) -self.CallScheduler:Clear() -end -function FSM_CONTROLLABLE:SetControllable(FSMControllable) -self.Controllable=FSMControllable -end -function FSM_CONTROLLABLE:GetControllable() -return self.Controllable -end -function FSM_CONTROLLABLE:_call_handler(step,trigger,params,EventName) -local handler=step..trigger -local ErrorHandler=function(errmsg) -env.info("Error in SCHEDULER function:"..errmsg) -if BASE.Debug~=nil then -env.info(BASE.Debug.traceback()) -end -return errmsg -end -if self[handler]then -self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3].." *** TaskUnit: "..self.Controllable:GetName()) -self._EventSchedules[EventName]=nil -local Result,Value=xpcall(function()return self[handler](self,self.Controllable,unpack(params))end,ErrorHandler) -return Value -end -end -end -do -FSM_PROCESS={ -ClassName="FSM_PROCESS", -} -function FSM_PROCESS:New(Controllable,Task) -local self=BASE:Inherit(self,FSM_CONTROLLABLE:New()) -self:Assign(Controllable,Task) -return self -end -function FSM_PROCESS:Init(FsmProcess) -self:T("No Initialisation") -end -function FSM_PROCESS:_call_handler(step,trigger,params,EventName) -local handler=step..trigger -local ErrorHandler=function(errmsg) -env.info("Error in FSM_PROCESS call handler:"..errmsg) -if BASE.Debug~=nil then -env.info(BASE.Debug.traceback()) -end -return errmsg -end -if self[handler]then -if handler~="onstatechange"then -self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3].." *** Task: "..self.Task:GetName()..", TaskUnit: "..self.Controllable:GetName()) -end -self._EventSchedules[EventName]=nil -local Result,Value -if self.Controllable and self.Controllable:IsAlive()==true then -Result,Value=xpcall(function()return self[handler](self,self.Controllable,self.Task,unpack(params))end,ErrorHandler) -end -return Value -end -end -function FSM_PROCESS:Copy(Controllable,Task) -self:T({self:GetClassNameAndID()}) -local NewFsm=self:New(Controllable,Task) -NewFsm:Assign(Controllable,Task) -NewFsm:Init(self) -NewFsm:SetStartState(self:GetStartState()) -for TransitionID,Transition in pairs(self:GetTransitions())do -NewFsm:AddTransition(Transition.From,Transition.Event,Transition.To) -end -for ProcessID,Process in pairs(self:GetProcesses())do -local FsmProcess=NewFsm:AddProcess(Process.From,Process.Event,Process.fsm:Copy(Controllable,Task),Process.ReturnEvents) -end -for EndStateID,EndState in pairs(self:GetEndStates())do -self:T(EndState) -NewFsm:AddEndState(EndState) -end -for ScoreID,Score in pairs(self:GetScores())do -self:T(Score) -NewFsm:AddScore(ScoreID,Score.ScoreText,Score.Score) -end -return NewFsm -end -function FSM_PROCESS:Remove() -self:F({self:GetClassNameAndID()}) -self:F("Clearing Schedules") -self.CallScheduler:Clear() -for ProcessID,Process in pairs(self:GetProcesses())do -if Process.fsm then -Process.fsm:Remove() -Process.fsm=nil -end -end -return self -end -function FSM_PROCESS:SetTask(Task) -self.Task=Task -return self -end -function FSM_PROCESS:GetTask() -return self.Task -end -function FSM_PROCESS:GetMission() -return self.Task.Mission -end -function FSM_PROCESS:GetCommandCenter() -return self:GetTask():GetMission():GetCommandCenter() -end -function FSM_PROCESS:Message(Message) -self:F({Message=Message}) -local CC=self:GetCommandCenter() -local TaskGroup=self.Controllable:GetGroup() -local PlayerName=self.Controllable:GetPlayerName() -PlayerName=PlayerName and" ("..PlayerName..")"or"" -local Callsign=self.Controllable:GetCallsign() -local Prefix=Callsign and" @ "..Callsign..PlayerName or"" -Message=Prefix..": "..Message -CC:MessageToGroup(Message,TaskGroup) -end -function FSM_PROCESS:Assign(ProcessUnit,Task) -self:SetControllable(ProcessUnit) -self:SetTask(Task) -return self -end -function FSM_PROCESS:onenterFailed(ProcessUnit,Task,From,Event,To) -self:T("*** FSM *** Failed *** "..Task:GetName().."/"..ProcessUnit:GetName().." *** "..From.." --> "..Event.." --> "..To) -self.Task:Fail() -end -function FSM_PROCESS:onstatechange(ProcessUnit,Task,From,Event,To) -if From~=To then -self:T("*** FSM *** Change *** "..Task:GetName().."/"..ProcessUnit:GetName().." *** "..From.." --> "..Event.." --> "..To) -end -if self._Scores[To]then -local Task=self.Task -local Scoring=Task:GetScoring() -if Scoring then -Scoring:_AddMissionTaskScore(Task.Mission,ProcessUnit,self._Scores[To].ScoreText,self._Scores[To].Score) -end -end -end -end -do -FSM_TASK={ -ClassName="FSM_TASK", -} -function FSM_TASK:New(TaskName) -local self=BASE:Inherit(self,FSM_CONTROLLABLE:New()) -self["onstatechange"]=self.OnStateChange -self.TaskName=TaskName -return self -end -function FSM_TASK:_call_handler(step,trigger,params,EventName) -local handler=step..trigger -local ErrorHandler=function(errmsg) -env.info("Error in SCHEDULER function:"..errmsg) -if BASE.Debug~=nil then -env.info(BASE.Debug.traceback()) -end -return errmsg -end -if self[handler]then -self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3].." *** Task: "..self.TaskName) -self._EventSchedules[EventName]=nil -local Result,Value=xpcall(function()return self[handler](self,unpack(params))end,ErrorHandler) -return Value -end -end -end -do -FSM_SET={ -ClassName="FSM_SET", -} -function FSM_SET:New(FSMSet) -self=BASE:Inherit(self,FSM:New()) -if FSMSet then -self:Set(FSMSet) -end -return self -end -function FSM_SET:Set(FSMSet) -self:F(FSMSet) -self.Set=FSMSet -end -function FSM_SET:Get() -return self.Controllable -end -function FSM_SET:_call_handler(step,trigger,params,EventName) -local handler=step..trigger -if self[handler]then -self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3]) -self._EventSchedules[EventName]=nil -return self[handler](self,self.Set,unpack(params)) -end -end -end -RADIO={ -ClassName="RADIO", -FileName="", -Frequency=0, -Modulation=radio.modulation.AM, -Subtitle="", -SubtitleDuration=0, -Power=100, -Loop=false, -alias=nil, -} -function RADIO:New(Positionable) -local self=BASE:Inherit(self,BASE:New()) -self:F(Positionable) -if Positionable:GetPointVec2()then -self.Positionable=Positionable -return self -end -self:E({error="The passed positionable is invalid, no RADIO created!",positionable=Positionable}) -return nil -end -function RADIO:SetAlias(alias) -self.alias=tostring(alias) -return self -end -function RADIO:GetAlias() -return tostring(self.alias) -end -function RADIO:SetFileName(FileName) -self:F2(FileName) -if type(FileName)=="string"then -if FileName:find(".ogg")or FileName:find(".wav")then -if not FileName:find("l10n/DEFAULT/")then -FileName="l10n/DEFAULT/"..FileName -end -self.FileName=FileName -return self -end -end -self:E({"File name invalid. Maybe something wrong with the extension?",FileName}) -return self -end -function RADIO:SetFrequency(Frequency) -self:F2(Frequency) -if type(Frequency)=="number"then -if(Frequency>=30 and Frequency<=87.995)or(Frequency>=108 and Frequency<=173.995)or(Frequency>=225 and Frequency<=399.975)then -self.Frequency=Frequency*1000000 -if self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP"then -local commandSetFrequency={ -id="SetFrequency", -params={ -frequency=self.Frequency, -modulation=self.Modulation, -} -} -self:T2(commandSetFrequency) -self.Positionable:SetCommand(commandSetFrequency) -end -return self -end -end -self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.",Frequency}) -return self -end -function RADIO:SetModulation(Modulation) -self:F2(Modulation) -if type(Modulation)=="number"then -if Modulation==radio.modulation.AM or Modulation==radio.modulation.FM then -self.Modulation=Modulation -return self -end -end -self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.",self.Modulation}) -return self -end -function RADIO:SetPower(Power) -self:F2(Power) -if type(Power)=="number"then -self.Power=math.floor(math.abs(Power)) -else -self:E({"Power is invalid. Power unchanged.",self.Power}) -end -return self -end -function RADIO:SetLoop(Loop) -self:F2(Loop) -if type(Loop)=="boolean"then -self.Loop=Loop -return self -end -self:E({"Loop is invalid. Loop unchanged.",self.Loop}) -return self -end -function RADIO:SetSubtitle(Subtitle,SubtitleDuration) -self:F2({Subtitle,SubtitleDuration}) -if type(Subtitle)=="string"then -self.Subtitle=Subtitle -else -self.Subtitle="" -self:E({"Subtitle is invalid. Subtitle reset.",self.Subtitle}) -end -if type(SubtitleDuration)=="number"then -self.SubtitleDuration=SubtitleDuration -else -self.SubtitleDuration=0 -self:E({"SubtitleDuration is invalid. SubtitleDuration reset.",self.SubtitleDuration}) -end -return self -end -function RADIO:NewGenericTransmission(FileName,Frequency,Modulation,Power,Loop) -self:F({FileName,Frequency,Modulation,Power}) -self:SetFileName(FileName) -if Frequency then self:SetFrequency(Frequency)end -if Modulation then self:SetModulation(Modulation)end -if Power then self:SetPower(Power)end -if Loop then self:SetLoop(Loop)end -return self -end -function RADIO:NewUnitTransmission(FileName,Subtitle,SubtitleDuration,Frequency,Modulation,Loop) -self:F({FileName,Subtitle,SubtitleDuration,Frequency,Modulation,Loop}) -self:SetFileName(FileName) -if Modulation then -self:SetModulation(Modulation) -end -if Frequency then -self:SetFrequency(Frequency) -end -if Subtitle then -self:SetSubtitle(Subtitle,SubtitleDuration or 0) -end -if Loop then -self:SetLoop(Loop) -end -return self -end -function RADIO:Broadcast(viatrigger) -self:F({viatrigger=viatrigger}) -if(self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP")and(not viatrigger)then -self:T("Broadcasting from a UNIT or a GROUP") -local commandTransmitMessage={ -id="TransmitMessage", -params={ -file=self.FileName, -duration=self.SubtitleDuration, -subtitle=self.Subtitle, -loop=self.Loop, -}} -self:T3(commandTransmitMessage) -self.Positionable:SetCommand(commandTransmitMessage) -else -self:T("Broadcasting from a POSITIONABLE") -trigger.action.radioTransmission(self.FileName,self.Positionable:GetPositionVec3(),self.Modulation,self.Loop,self.Frequency,self.Power,tostring(self.ID)) -end -return self -end -function RADIO:StopBroadcast() -self:F() -if self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP"then -local commandStopTransmission={id="StopTransmission",params={}} -self.Positionable:SetCommand(commandStopTransmission) -else -trigger.action.stopRadioTransmission(tostring(self.ID)) -end -return self -end -BEACON={ -ClassName="BEACON", -Positionable=nil, -name=nil, -} -BEACON.Type={ -NULL=0, -VOR=1, -DME=2, -VOR_DME=3, -TACAN=4, -VORTAC=5, -RSBN=128, -BROADCAST_STATION=1024, -HOMER=8, -AIRPORT_HOMER=4104, -AIRPORT_HOMER_WITH_MARKER=4136, -ILS_FAR_HOMER=16408, -ILS_NEAR_HOMER=16424, -ILS_LOCALIZER=16640, -ILS_GLIDESLOPE=16896, -PRMG_LOCALIZER=33024, -PRMG_GLIDESLOPE=33280, -ICLS=131584, -ICLS_LOCALIZER=131328, -ICLS_GLIDESLOPE=131584, -NAUTICAL_HOMER=65536, -} -BEACON.System={ -PAR_10=1, -RSBN_5=2, -TACAN=3, -TACAN_TANKER_X=4, -TACAN_TANKER_Y=5, -VOR=6, -ILS_LOCALIZER=7, -ILS_GLIDESLOPE=8, -PRMG_LOCALIZER=9, -PRMG_GLIDESLOPE=10, -BROADCAST_STATION=11, -VORTAC=12, -TACAN_AA_MODE_X=13, -TACAN_AA_MODE_Y=14, -VORDME=15, -ICLS_LOCALIZER=16, -ICLS_GLIDESLOPE=17, -} -function BEACON:New(Positionable) -local self=BASE:Inherit(self,BASE:New()) -self:F(Positionable) -if Positionable:GetPointVec2()then -self.Positionable=Positionable -self.name=Positionable:GetName() -self:I(string.format("New BEACON %s",tostring(self.name))) -return self -end -self:E({"The passed positionable is invalid, no BEACON created",Positionable}) -return nil -end -function BEACON:ActivateTACAN(Channel,Mode,Message,Bearing,Duration) -self:T({channel=Channel,mode=Mode,callsign=Message,bearing=Bearing,duration=Duration}) -local Frequency=UTILS.TACANToFrequency(Channel,Mode) -if not Frequency then -self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"}) -return self -end -local Type=BEACON.Type.TACAN -local System=BEACON.System.TACAN -local AA=self.Positionable:IsAir() -if AA then -System=5 -if Mode~="Y"then -self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y !The BEACON is not emitting.",self.Positionable}) -end -end -local UnitID=self.Positionable:GetID() -self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!",tostring(self.name),Channel,Mode,Message,tostring(Bearing),tostring(Duration))}) -self.Positionable:CommandActivateBeacon(Type,System,Frequency,UnitID,Channel,Mode,AA,Message,Bearing) -if Duration then -self.Positionable:DeactivateBeacon(Duration) -end -return self -end -function BEACON:ActivateICLS(Channel,Callsign,Duration) -self:F({Channel=Channel,Callsign=Callsign,Duration=Duration}) -local UnitID=self.Positionable:GetID() -self:T2({"ICLS BEACON started!"}) -self.Positionable:CommandActivateICLS(Channel,UnitID,Callsign) -if Duration then -self.Positionable:DeactivateBeacon(Duration) -end -return self -end -function BEACON:AATACAN(TACANChannel,Message,Bearing,BeaconDuration) -self:F({TACANChannel,Message,Bearing,BeaconDuration}) -local IsValid=true -if not self.Positionable:IsAir()then -self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting",self.Positionable}) -IsValid=false -end -local Frequency=self:_TACANToFrequency(TACANChannel,"Y") -if not Frequency then -self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"}) -IsValid=false -end -local System -if Bearing then -System=5 -else -System=14 -end -if IsValid then -self:T2({"AA TACAN BEACON started !"}) -self.Positionable:SetCommand({ -id="ActivateBeacon", -params={ -type=4, -system=System, -callsign=Message, -frequency=Frequency, -} -}) -if BeaconDuration then -SCHEDULER:New(nil, -function() -self:StopAATACAN() -end,{},BeaconDuration) -end -end -return self -end -function BEACON:StopAATACAN() -self:F() -if not self.Positionable then -self:E({"Start the beacon first before stoping it !"}) -else -self.Positionable:SetCommand({ -id='DeactivateBeacon', -params={ -} -}) -end -end -function BEACON:RadioBeacon(FileName,Frequency,Modulation,Power,BeaconDuration) -self:F({FileName,Frequency,Modulation,Power,BeaconDuration}) -local IsValid=false -if type(FileName)=="string"then -if FileName:find(".ogg")or FileName:find(".wav")then -if not FileName:find("l10n/DEFAULT/")then -FileName="l10n/DEFAULT/"..FileName -end -IsValid=true -end -end -if not IsValid then -self:E({"File name invalid. Maybe something wrong with the extension ? ",FileName}) -end -if type(Frequency)~="number"and IsValid then -self:E({"Frequency invalid. ",Frequency}) -IsValid=false -end -Frequency=Frequency*1000000 -if Modulation~=radio.modulation.AM and Modulation~=radio.modulation.FM and IsValid then -self:E({"Modulation is invalid. Use DCS's enum radio.modulation.",Modulation}) -IsValid=false -end -if type(Power)~="number"and IsValid then -self:E({"Power is invalid. ",Power}) -IsValid=false -end -Power=math.floor(math.abs(Power)) -if IsValid then -self:T2({"Activating Beacon on ",Frequency,Modulation}) -trigger.action.radioTransmission(FileName,self.Positionable:GetPositionVec3(),Modulation,true,Frequency,Power,tostring(self.ID)) -if BeaconDuration then -SCHEDULER:New(nil, -function() -self:StopRadioBeacon() -end,{},BeaconDuration) -end -end -end -function BEACON:StopRadioBeacon() -self:F() -trigger.action.stopRadioTransmission(tostring(self.ID)) -return self -end -function BEACON:_TACANToFrequency(TACANChannel,TACANMode) -self:F3({TACANChannel,TACANMode}) -if type(TACANChannel)~="number"then -if TACANMode~="X"and TACANMode~="Y"then -return nil -end -end -local A=1151 -local B=64 -if TACANChannel<64 then -B=1 -end -if TACANMode=='Y'then -A=1025 -if TACANChannel<64 then -A=1088 -end -else -if TACANChannel<64 then -A=962 -end -end -return(A+TACANChannel-B)*1000000 -end -RADIOQUEUE={ -ClassName="RADIOQUEUE", -Debugmode=nil, -lid=nil, -frequency=nil, -modulation=nil, -scheduler=nil, -RQid=nil, -queue={}, -alias=nil, -dt=nil, -delay=nil, -Tlast=nil, -sendercoord=nil, -sendername=nil, -senderinit=nil, -power=nil, -numbers={}, -checking=nil, -schedonce=false, -} -function RADIOQUEUE:New(frequency,modulation,alias) -local self=BASE:Inherit(self,BASE:New()) -self.alias=alias or"My Radio" -self.lid=string.format("RADIOQUEUE %s | ",self.alias) -if frequency==nil then -self:E(self.lid.."ERROR: No frequency specified as first parameter!") -return nil -end -self.frequency=frequency*1000000 -self.modulation=modulation or radio.modulation.AM -self:SetRadioPower() -self.scheduler=SCHEDULER:New() -self.scheduler:NoTrace() -return self -end -function RADIOQUEUE:Start(delay,dt) -self.delay=delay or 1 -self.dt=dt or 0.01 -self:I(self.lid..string.format("Starting RADIOQUEUE %s on Frequency %.2f MHz [modulation=%d] in %.1f seconds (dt=%.3f sec)",self.alias,self.frequency/1000000,self.modulation,self.delay,self.dt)) -if self.schedonce then -self:_CheckRadioQueueDelayed(delay) -else -self.RQid=self.scheduler:Schedule(nil,RADIOQUEUE._CheckRadioQueue,{self},delay,dt) -end -return self -end -function RADIOQUEUE:Stop() -self:I(self.lid.."Stopping RADIOQUEUE.") -self.scheduler:Stop(self.RQid) -self.queue={} -return self -end -function RADIOQUEUE:SetSenderCoordinate(coordinate) -self.sendercoord=coordinate -return self -end -function RADIOQUEUE:SetSenderUnitName(name) -self.sendername=name -return self -end -function RADIOQUEUE:SetRadioPower(power) -self.power=power or 100 -return self -end -function RADIOQUEUE:SetDigit(digit,filename,duration,path,subtitle,subduration) -local transmission={} -transmission.filename=filename -transmission.duration=duration -transmission.path=path or"l10n/DEFAULT/" -transmission.subtitle=nil -transmission.subduration=nil -if type(digit)=="number"then -digit=tostring(digit) -end -self.numbers[digit]=transmission -return self -end -function RADIOQUEUE:AddTransmission(transmission) -self:F({transmission=transmission}) -transmission.isplaying=false -transmission.Tstarted=nil -table.insert(self.queue,transmission) -if self.schedonce and not self.checking then -self:_CheckRadioQueueDelayed() -end -return self -end -function RADIOQUEUE:NewTransmission(filename,duration,path,tstart,interval,subtitle,subduration) -if not filename then -self:E(self.lid.."ERROR: No filename specified.") -return nil -end -if type(filename)~="string"then -self:E(self.lid.."ERROR: Filename specified is NOT a string.") -return nil -end -if not duration then -self:E(self.lid.."ERROR: No duration of transmission specified.") -return nil -end -if type(duration)~="number"then -self:E(self.lid.."ERROR: Duration specified is NOT a number.") -return nil -end -local transmission={} -transmission.filename=filename -transmission.duration=duration -transmission.path=path or"l10n/DEFAULT/" -transmission.Tplay=tstart or timer.getAbsTime() -transmission.subtitle=subtitle -transmission.interval=interval or 0 -if transmission.subtitle then -transmission.subduration=subduration or 5 -else -transmission.subduration=nil -end -self:AddTransmission(transmission) -return self -end -function RADIOQUEUE:Number2Transmission(number,delay,interval) -local function _split(str) -local chars={} -for i=1,#str do -local c=str:sub(i,i) -table.insert(chars,c) -end -return chars -end -local numbers=_split(number) -local wait=0 -for i=1,#numbers do -local n=numbers[i] -local transmission=UTILS.DeepCopy(self.numbers[n]) -transmission.Tplay=timer.getAbsTime()+(delay or 0) -if interval and i==1 then -transmission.interval=interval -end -self:AddTransmission(transmission) -wait=wait+transmission.duration -end -return wait -end -function RADIOQUEUE:Broadcast(transmission) -local sender=self:_GetRadioSender() -local filename=string.format("%s%s",transmission.path,transmission.filename) -if sender then -self:T(self.lid..string.format("Broadcasting from aircraft %s",sender:GetName())) -if not self.senderinit then -local commandFrequency={ -id="SetFrequency", -params={ -frequency=self.frequency, -modulation=self.modulation, -}} -sender:SetCommand(commandFrequency) -self.senderinit=true -end -local subtitle=nil -local duration=nil -if transmission.subtitle and transmission.subduration and transmission.subduration>0 then -subtitle=transmission.subtitle -duration=transmission.subduration -end -local commandTransmit={ -id="TransmitMessage", -params={ -file=filename, -duration=duration, -subtitle=subtitle, -loop=false, -}} -sender:SetCommand(commandTransmit) -if self.Debugmode then -local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s",filename,self.frequency/1000000,transmission.duration,transmission.subtitle or"") -MESSAGE:New(text,2,"RADIOQUEUE "..self.alias):ToAll() -end -else -self:T(self.lid..string.format("Broadcasting via trigger.action.radioTransmission().")) -local vec3=nil -if self.sendername then -vec3=self:_GetRadioSenderCoord() -end -if self.sendercoord and not vec3 then -vec3=self.sendercoord:GetVec3() -end -if vec3 then -self:T("Sending") -self:T({filename=filename,vec3=vec3,modulation=self.modulation,frequency=self.frequency,power=self.power}) -trigger.action.radioTransmission(filename,vec3,self.modulation,false,self.frequency,self.power) -if self.Debugmode then -local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s",filename,self.frequency/1000000,transmission.duration,transmission.subtitle or"") -MESSAGE:New(string.format(text,filename,transmission.duration,transmission.subtitle or""),5,"RADIOQUEUE "..self.alias):ToAll() -end -end -end -end -function RADIOQUEUE:_CheckRadioQueueDelayed(delay) -self.checking=true -self:ScheduleOnce(delay or self.dt,RADIOQUEUE._CheckRadioQueue,self) -end -function RADIOQUEUE:_CheckRadioQueue() -if#self.queue==0 then -self.checking=false -return -end -local time=timer.getAbsTime() -local playing=false -local next=nil -local remove=nil -for i,_transmission in ipairs(self.queue)do -local transmission=_transmission -if time>=transmission.Tplay then -if transmission.isplaying then -if time>=transmission.Tstarted+transmission.duration then -transmission.isplaying=false -remove=i -self.Tlast=time -else -playing=true -end -else -local Tlast=self.Tlast -if transmission.interval==nil then -if next==nil then -next=transmission -end -else -if Tlast==nil or time-Tlast>=transmission.interval then -next=transmission -else -end -end -if next or Tlast then -break -end -end -else -end -end -if next~=nil and not playing then -self:Broadcast(next) -next.isplaying=true -next.Tstarted=time -end -if remove then -table.remove(self.queue,remove) -end -if self.schedonce then -self:_CheckRadioQueueDelayed() -end -end -function RADIOQUEUE:_GetRadioSender() -local sender=nil -if self.sendername then -sender=UNIT:FindByName(self.sendername) -if sender and sender:IsAlive()and(sender:IsAir()or sender:IsGround())then -return sender -end -end -return nil -end -function RADIOQUEUE:_GetRadioSenderCoord() -local vec3=nil -if self.sendername then -local sender=UNIT:FindByName(self.sendername) -if sender and sender:IsAlive()then -return sender:GetVec3() -end -local sender=STATIC:FindByName(self.sendername,false) -if sender then -return sender:GetVec3() -end -end -return nil -end -RADIOSPEECH={ -ClassName="RADIOSPEECH", -Vocabulary={ -EN={}, -DE={}, -RU={}, -} -} -RADIOSPEECH.Vocabulary.EN={ -["1"]={"1",0.25}, -["2"]={"2",0.25}, -["3"]={"3",0.30}, -["4"]={"4",0.35}, -["5"]={"5",0.35}, -["6"]={"6",0.42}, -["7"]={"7",0.38}, -["8"]={"8",0.20}, -["9"]={"9",0.32}, -["10"]={"10",0.35}, -["11"]={"11",0.40}, -["12"]={"12",0.42}, -["13"]={"13",0.38}, -["14"]={"14",0.42}, -["15"]={"15",0.42}, -["16"]={"16",0.52}, -["17"]={"17",0.59}, -["18"]={"18",0.40}, -["19"]={"19",0.47}, -["20"]={"20",0.38}, -["30"]={"30",0.29}, -["40"]={"40",0.35}, -["50"]={"50",0.32}, -["60"]={"60",0.44}, -["70"]={"70",0.48}, -["80"]={"80",0.26}, -["90"]={"90",0.36}, -["100"]={"100",0.55}, -["200"]={"200",0.55}, -["300"]={"300",0.61}, -["400"]={"400",0.60}, -["500"]={"500",0.61}, -["600"]={"600",0.65}, -["700"]={"700",0.70}, -["800"]={"800",0.54}, -["900"]={"900",0.60}, -["1000"]={"1000",0.60}, -["2000"]={"2000",0.61}, -["3000"]={"3000",0.64}, -["4000"]={"4000",0.62}, -["5000"]={"5000",0.69}, -["6000"]={"6000",0.69}, -["7000"]={"7000",0.75}, -["8000"]={"8000",0.59}, -["9000"]={"9000",0.65}, -["chevy"]={"chevy",0.35}, -["colt"]={"colt",0.35}, -["springfield"]={"springfield",0.65}, -["dodge"]={"dodge",0.35}, -["enfield"]={"enfield",0.5}, -["ford"]={"ford",0.32}, -["pontiac"]={"pontiac",0.55}, -["uzi"]={"uzi",0.28}, -["degrees"]={"degrees",0.5}, -["kilometers"]={"kilometers",0.65}, -["km"]={"kilometers",0.65}, -["miles"]={"miles",0.45}, -["meters"]={"meters",0.41}, -["mi"]={"miles",0.45}, -["feet"]={"feet",0.29}, -["br"]={"br",1.1}, -["bra"]={"bra",0.3}, -["returning to base"]={"returning_to_base",0.85}, -["on route to ground target"]={"on_route_to_ground_target",1.05}, -["intercepting bogeys"]={"intercepting_bogeys",1.00}, -["engaging ground target"]={"engaging_ground_target",1.20}, -["engaging bogeys"]={"engaging_bogeys",0.81}, -["wheels up"]={"wheels_up",0.42}, -["landing at base"]={"landing at base",0.8}, -["patrolling"]={"patrolling",0.55}, -["for"]={"for",0.31}, -["and"]={"and",0.31}, -["at"]={"at",0.3}, -["dot"]={"dot",0.26}, -["defender"]={"defender",0.45}, -} -RADIOSPEECH.Vocabulary.RU={ -["1"]={"1",0.34}, -["2"]={"2",0.30}, -["3"]={"3",0.23}, -["4"]={"4",0.51}, -["5"]={"5",0.31}, -["6"]={"6",0.44}, -["7"]={"7",0.25}, -["8"]={"8",0.43}, -["9"]={"9",0.45}, -["10"]={"10",0.53}, -["11"]={"11",0.66}, -["12"]={"12",0.70}, -["13"]={"13",0.66}, -["14"]={"14",0.80}, -["15"]={"15",0.65}, -["16"]={"16",0.75}, -["17"]={"17",0.74}, -["18"]={"18",0.85}, -["19"]={"19",0.80}, -["20"]={"20",0.58}, -["30"]={"30",0.51}, -["40"]={"40",0.51}, -["50"]={"50",0.67}, -["60"]={"60",0.76}, -["70"]={"70",0.68}, -["80"]={"80",0.84}, -["90"]={"90",0.71}, -["100"]={"100",0.35}, -["200"]={"200",0.59}, -["300"]={"300",0.53}, -["400"]={"400",0.70}, -["500"]={"500",0.50}, -["600"]={"600",0.58}, -["700"]={"700",0.64}, -["800"]={"800",0.77}, -["900"]={"900",0.75}, -["1000"]={"1000",0.87}, -["2000"]={"2000",0.83}, -["3000"]={"3000",0.84}, -["4000"]={"4000",1.00}, -["5000"]={"5000",0.77}, -["6000"]={"6000",0.90}, -["7000"]={"7000",0.77}, -["8000"]={"8000",0.92}, -["9000"]={"9000",0.87}, -["степени"]={"degrees",0.5}, -["километров"]={"kilometers",0.65}, -["km"]={"kilometers",0.65}, -["миль"]={"miles",0.45}, -["mi"]={"miles",0.45}, -["метры"]={"meters",0.41}, -["m"]={"meters",0.41}, -["ноги"]={"feet",0.37}, -["br"]={"br",1.1}, -["bra"]={"bra",0.3}, -["возвращаясь на базу"]={"returning_to_base",1.40}, -["на пути к наземной цели"]={"on_route_to_ground_target",1.45}, -["перехват самолетов"]={"intercepting_bogeys",1.22}, -["поражение наземной цели"]={"engaging_ground_target",1.53}, -["захватывающие самолеты"]={"engaging_bogeys",1.68}, -["колеса вверх"]={"wheels_up",0.92}, -["посадка на базу"]={"landing at base",1.04}, -["патрулирующий"]={"patrolling",0.96}, -["за"]={"for",0.27}, -["и"]={"and",0.17}, -["в"]={"at",0.19}, -["dot"]={"dot",0.51}, -["defender"]={"defender",0.45}, -} -function RADIOSPEECH:New(frequency,modulation) -local self=BASE:Inherit(self,RADIOQUEUE:New(frequency,modulation)) -self.Language="EN" -self:BuildTree() -return self -end -function RADIOSPEECH:SetLanguage(Langauge) -self.Language=Langauge -end -function RADIOSPEECH:AddSentenceToSpeech(RemainingSentence,Speech,Sentence,Data) -self:I({RemainingSentence,Speech,Sentence,Data}) -local Token,RemainingSentence=RemainingSentence:match("^ *([^ ]+)(.*)") -self:I({Token=Token,RemainingSentence=RemainingSentence}) -if Token then -if not Speech[Token]then -Speech[Token]={} -if RemainingSentence and RemainingSentence~=""then -Speech[Token].Next={} -self:AddSentenceToSpeech(RemainingSentence,Speech[Token].Next,Sentence,Data) -else -Speech[Token].Sentence=Sentence -Speech[Token].Data=Data -end -end -end -end -function RADIOSPEECH:BuildTree() -self.Speech={} -for Language,Sentences in pairs(self.Vocabulary)do -self:I({Language=Language,Sentences=Sentences}) -self.Speech[Language]={} -for Sentence,Data in pairs(Sentences)do -self:I({Sentence=Sentence,Data=Data}) -self:AddSentenceToSpeech(Sentence,self.Speech[Language],Sentence,Data) -end -end -self:I({Speech=self.Speech}) -return self -end -function RADIOSPEECH:SpeakWords(Sentence,Speech,Language) -local OriginalSentence=Sentence -local Word,RemainderSentence=Sentence:match("^[., ]*([^ .,]+)(.*)") -self:I({Word=Word,Speech=Speech[Word],RemainderSentence=RemainderSentence}) -if Word then -if Word~=""and tonumber(Word)==nil then -Word=Word:lower() -if Speech[Word]then -if Speech[Word].Next==nil then -self:I({Sentence=Speech[Word].Sentence,Data=Speech[Word].Data}) -self:NewTransmission(Speech[Word].Data[1]..".wav",Speech[Word].Data[2],Language.."/") -else -if RemainderSentence and RemainderSentence~=""then -return self:SpeakWords(RemainderSentence,Speech[Word].Next,Language) -end -end -end -return RemainderSentence -end -return OriginalSentence -else -return"" -end -end -function RADIOSPEECH:SpeakDigits(Sentence,Speech,Langauge) -local OriginalSentence=Sentence -local Digits,RemainderSentence=Sentence:match("^[., ]*([^ .,]+)(.*)") -self:I({Digits=Digits,Speech=Speech[Digits],RemainderSentence=RemainderSentence}) -if Digits then -if Digits~=""and tonumber(Digits)~=nil then -local Number=tonumber(Digits) -local Multiple=nil -while Number>=0 do -if Number>1000 then -Multiple=math.floor(Number/1000)*1000 -elseif Number>100 then -Multiple=math.floor(Number/100)*100 -elseif Number>20 then -Multiple=math.floor(Number/10)*10 -elseif Number>=0 then -Multiple=Number -end -Sentence=tostring(Multiple) -if Speech[Sentence]then -self:I({Speech=Speech[Sentence].Sentence,Data=Speech[Sentence].Data}) -self:NewTransmission(Speech[Sentence].Data[1]..".wav",Speech[Sentence].Data[2],Langauge.."/") -end -Number=Number-Multiple -Number=(Number==0)and-1 or Number -end -return RemainderSentence -end -return OriginalSentence -else -return"" -end -end -function RADIOSPEECH:Speak(Sentence,Language) -self:I({Sentence,Language}) -local Language=Language or"EN" -self:I({Language=Language}) -local Speech=self.Speech[Language] -self:I({Speech=Speech,Language=Language}) -self:NewTransmission("_In.wav",0.52,Language.."/") -repeat -Sentence=self:SpeakWords(Sentence,Speech,Language) -self:I({Sentence=Sentence}) -Sentence=self:SpeakDigits(Sentence,Speech,Language) -self:I({Sentence=Sentence}) -until not Sentence or Sentence=="" -self:NewTransmission("_Out.wav",0.28,Language.."/") -end -SPAWN={ -ClassName="SPAWN", -SpawnTemplatePrefix=nil, -SpawnAliasPrefix=nil, -} -SPAWN.Takeoff={ -Air=1, -Runway=2, -Hot=3, -Cold=4, -} -function SPAWN:New(SpawnTemplatePrefix) -local self=BASE:Inherit(self,BASE:New()) -self:F({SpawnTemplatePrefix}) -local TemplateGroup=GROUP:FindByName(SpawnTemplatePrefix) -if TemplateGroup then -self.SpawnTemplatePrefix=SpawnTemplatePrefix -self.SpawnIndex=0 -self.SpawnCount=0 -self.AliveUnits=0 -self.SpawnIsScheduled=false -self.SpawnTemplate=self._GetTemplate(self,SpawnTemplatePrefix) -self.Repeat=false -self.UnControlled=false -self.SpawnInitLimit=false -self.SpawnMaxUnitsAlive=0 -self.SpawnMaxGroups=0 -self.SpawnRandomize=false -self.SpawnVisible=false -self.AIOnOff=true -self.SpawnUnControlled=false -self.SpawnInitKeepUnitNames=false -self.DelayOnOff=false -self.SpawnGrouping=nil -self.SpawnInitLivery=nil -self.SpawnInitSkill=nil -self.SpawnInitFreq=nil -self.SpawnInitModu=nil -self.SpawnInitRadio=nil -self.SpawnInitModex=nil -self.SpawnInitAirbase=nil -self.TweakedTemplate=false -self.SpawnGroups={} -else -error("SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'") -end -self:SetEventPriority(5) -self.SpawnHookScheduler=SCHEDULER:New(nil) -return self -end -function SPAWN:NewWithAlias(SpawnTemplatePrefix,SpawnAliasPrefix) -local self=BASE:Inherit(self,BASE:New()) -self:F({SpawnTemplatePrefix,SpawnAliasPrefix}) -local TemplateGroup=GROUP:FindByName(SpawnTemplatePrefix) -if TemplateGroup then -self.SpawnTemplatePrefix=SpawnTemplatePrefix -self.SpawnAliasPrefix=SpawnAliasPrefix -self.SpawnIndex=0 -self.SpawnCount=0 -self.AliveUnits=0 -self.SpawnIsScheduled=false -self.SpawnTemplate=self._GetTemplate(self,SpawnTemplatePrefix) -self.Repeat=false -self.UnControlled=false -self.SpawnInitLimit=false -self.SpawnMaxUnitsAlive=0 -self.SpawnMaxGroups=0 -self.SpawnRandomize=false -self.SpawnVisible=false -self.AIOnOff=true -self.SpawnUnControlled=false -self.SpawnInitKeepUnitNames=false -self.DelayOnOff=false -self.SpawnGrouping=nil -self.SpawnInitLivery=nil -self.SpawnInitSkill=nil -self.SpawnInitFreq=nil -self.SpawnInitModu=nil -self.SpawnInitRadio=nil -self.SpawnInitModex=nil -self.SpawnInitAirbase=nil -self.TweakedTemplate=false -self.SpawnGroups={} -else -error("SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'") -end -self:SetEventPriority(5) -self.SpawnHookScheduler=SCHEDULER:New(nil) -return self -end -function SPAWN:NewFromTemplate(SpawnTemplate,SpawnTemplatePrefix,SpawnAliasPrefix) -local self=BASE:Inherit(self,BASE:New()) -self:F({SpawnTemplate,SpawnTemplatePrefix,SpawnAliasPrefix}) -if SpawnAliasPrefix==nil or SpawnAliasPrefix==""then -BASE:I("ERROR: in function NewFromTemplate, required paramter SpawnAliasPrefix is not set") -return nil -end -if SpawnTemplate then -self.SpawnTemplate=SpawnTemplate -self.SpawnTemplatePrefix=SpawnTemplatePrefix -self.SpawnAliasPrefix=SpawnAliasPrefix -self.SpawnIndex=0 -self.SpawnCount=0 -self.AliveUnits=0 -self.SpawnIsScheduled=false -self.Repeat=false -self.UnControlled=false -self.SpawnInitLimit=false -self.SpawnMaxUnitsAlive=0 -self.SpawnMaxGroups=0 -self.SpawnRandomize=false -self.SpawnVisible=false -self.AIOnOff=true -self.SpawnUnControlled=false -self.SpawnInitKeepUnitNames=false -self.DelayOnOff=false -self.Grouping=nil -self.SpawnInitLivery=nil -self.SpawnInitSkill=nil -self.SpawnInitFreq=nil -self.SpawnInitModu=nil -self.SpawnInitRadio=nil -self.SpawnInitModex=nil -self.SpawnInitAirbase=nil -self.TweakedTemplate=true -self.SpawnGroups={} -else -error("There is no template provided for SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'") -end -self:SetEventPriority(5) -self.SpawnHookScheduler=SCHEDULER:New(nil) -return self -end -function SPAWN:InitLimit(SpawnMaxUnitsAlive,SpawnMaxGroups) -self:F({self.SpawnTemplatePrefix,SpawnMaxUnitsAlive,SpawnMaxGroups}) -self.SpawnInitLimit=true -self.SpawnMaxUnitsAlive=SpawnMaxUnitsAlive -self.SpawnMaxGroups=SpawnMaxGroups -for SpawnGroupID=1,self.SpawnMaxGroups do -self:_InitializeSpawnGroups(SpawnGroupID) -end -return self -end -function SPAWN:InitKeepUnitNames(KeepUnitNames) -self:F() -self.SpawnInitKeepUnitNames=KeepUnitNames or true -return self -end -function SPAWN:InitLateActivated(LateActivated) -self:F() -self.LateActivated=LateActivated or true -return self -end -function SPAWN:InitAirbase(AirbaseName,Takeoff,TerminalType) -self:F() -self.SpawnInitAirbase=AIRBASE:FindByName(AirbaseName) -self.SpawnInitTakeoff=Takeoff or SPAWN.Takeoff.Hot -self.SpawnInitTerminalType=TerminalType -return self -end -function SPAWN:InitHeading(HeadingMin,HeadingMax) -self:F() -self.SpawnInitHeadingMin=HeadingMin -self.SpawnInitHeadingMax=HeadingMax -return self -end -function SPAWN:InitGroupHeading(HeadingMin,HeadingMax,unitVar) -self:F({HeadingMin=HeadingMin,HeadingMax=HeadingMax,unitVar=unitVar}) -self.SpawnInitGroupHeadingMin=HeadingMin -self.SpawnInitGroupHeadingMax=HeadingMax -self.SpawnInitGroupUnitVar=unitVar -return self -end -function SPAWN:InitCoalition(Coalition) -self:F({coalition=Coalition}) -self.SpawnInitCoalition=Coalition -return self -end -function SPAWN:InitCountry(Country) -self:F() -self.SpawnInitCountry=Country -return self -end -function SPAWN:InitCategory(Category) -self:F() -self.SpawnInitCategory=Category -return self -end -function SPAWN:InitLivery(Livery) -self:F({livery=Livery}) -self.SpawnInitLivery=Livery -return self -end -function SPAWN:InitSkill(Skill) -self:F({skill=Skill}) -if Skill:lower()=="average"then -self.SpawnInitSkill="Average" -elseif Skill:lower()=="good"then -self.SpawnInitSkill="Good" -elseif Skill:lower()=="excellent"then -self.SpawnInitSkill="Excellent" -elseif Skill:lower()=="random"then -self.SpawnInitSkill="Random" -else -self.SpawnInitSkill="High" -end -return self -end -function SPAWN:InitRadioCommsOnOff(switch) -self:F({switch=switch}) -self.SpawnInitRadio=switch or true -return self -end -function SPAWN:InitRadioFrequency(frequency) -self:F({frequency=frequency}) -self.SpawnInitFreq=frequency -return self -end -function SPAWN:InitRadioModulation(modulation) -self:F({modulation=modulation}) -if modulation and modulation:lower()=="fm"then -self.SpawnInitModu=radio.modulation.FM -else -self.SpawnInitModu=radio.modulation.AM -end -return self -end -function SPAWN:InitModex(modex) -if modex then -self.SpawnInitModex=tonumber(modex) -end -return self -end -function SPAWN:InitRandomizeRoute(SpawnStartPoint,SpawnEndPoint,SpawnRadius,SpawnHeight) -self:F({self.SpawnTemplatePrefix,SpawnStartPoint,SpawnEndPoint,SpawnRadius,SpawnHeight}) -self.SpawnRandomizeRoute=true -self.SpawnRandomizeRouteStartPoint=SpawnStartPoint -self.SpawnRandomizeRouteEndPoint=SpawnEndPoint -self.SpawnRandomizeRouteRadius=SpawnRadius -self.SpawnRandomizeRouteHeight=SpawnHeight -for GroupID=1,self.SpawnMaxGroups do -self:_RandomizeRoute(GroupID) -end -return self -end -function SPAWN:InitRandomizePosition(RandomizePosition,OuterRadius,InnerRadius) -self:F({self.SpawnTemplatePrefix,RandomizePosition,OuterRadius,InnerRadius}) -self.SpawnRandomizePosition=RandomizePosition or false -self.SpawnRandomizePositionOuterRadius=OuterRadius or 0 -self.SpawnRandomizePositionInnerRadius=InnerRadius or 0 -for GroupID=1,self.SpawnMaxGroups do -self:_RandomizeRoute(GroupID) -end -return self -end -function SPAWN:InitRandomizeUnits(RandomizeUnits,OuterRadius,InnerRadius) -self:F({self.SpawnTemplatePrefix,RandomizeUnits,OuterRadius,InnerRadius}) -self.SpawnRandomizeUnits=RandomizeUnits or false -self.SpawnOuterRadius=OuterRadius or 0 -self.SpawnInnerRadius=InnerRadius or 0 -for GroupID=1,self.SpawnMaxGroups do -self:_RandomizeRoute(GroupID) -end -return self -end -function SPAWN:InitRandomizeTemplate(SpawnTemplatePrefixTable) -self:F({self.SpawnTemplatePrefix,SpawnTemplatePrefixTable}) -self.SpawnTemplatePrefixTable=SpawnTemplatePrefixTable -self.SpawnRandomizeTemplate=true -for SpawnGroupID=1,self.SpawnMaxGroups do -self:_RandomizeTemplate(SpawnGroupID) -end -return self -end -function SPAWN:InitRandomizeTemplateSet(SpawnTemplateSet) -self:F({self.SpawnTemplatePrefix}) -self.SpawnTemplatePrefixTable=SpawnTemplateSet:GetSetNames() -self.SpawnRandomizeTemplate=true -for SpawnGroupID=1,self.SpawnMaxGroups do -self:_RandomizeTemplate(SpawnGroupID) -end -return self -end -function SPAWN:InitRandomizeTemplatePrefixes(SpawnTemplatePrefixes) -self:F({self.SpawnTemplatePrefix}) -local SpawnTemplateSet=SET_GROUP:New():FilterPrefixes(SpawnTemplatePrefixes):FilterOnce() -self:InitRandomizeTemplateSet(SpawnTemplateSet) -return self -end -function SPAWN:InitGrouping(Grouping) -self:F({self.SpawnTemplatePrefix,Grouping}) -self.SpawnGrouping=Grouping -return self -end -function SPAWN:InitRandomizeZones(SpawnZoneTable) -self:F({self.SpawnTemplatePrefix,SpawnZoneTable}) -self.SpawnZoneTable=SpawnZoneTable -self.SpawnRandomizeZones=true -for SpawnGroupID=1,self.SpawnMaxGroups do -self:_RandomizeZones(SpawnGroupID) -end -return self -end -function SPAWN:InitRepeat() -self:F({self.SpawnTemplatePrefix,self.SpawnIndex}) -self.Repeat=true -self.RepeatOnEngineShutDown=false -self.RepeatOnLanding=true -return self -end -function SPAWN:InitRepeatOnLanding() -self:F({self.SpawnTemplatePrefix}) -self:InitRepeat() -self.RepeatOnEngineShutDown=false -self.RepeatOnLanding=true -return self -end -function SPAWN:InitRepeatOnEngineShutDown() -self:F({self.SpawnTemplatePrefix}) -self:InitRepeat() -self.RepeatOnEngineShutDown=true -self.RepeatOnLanding=false -return self -end -function SPAWN:InitCleanUp(SpawnCleanUpInterval) -self:F({self.SpawnTemplatePrefix,SpawnCleanUpInterval}) -self.SpawnCleanUpInterval=SpawnCleanUpInterval -self.SpawnCleanUpTimeStamps={} -local SpawnGroup,SpawnCursor=self:GetFirstAliveGroup() -self:T({"CleanUp Scheduler:",SpawnGroup}) -self.CleanUpScheduler=SCHEDULER:New(self,self._SpawnCleanUpScheduler,{},1,SpawnCleanUpInterval,0.2) -return self -end -function SPAWN:InitArray(SpawnAngle,SpawnWidth,SpawnDeltaX,SpawnDeltaY) -self:F({self.SpawnTemplatePrefix,SpawnAngle,SpawnWidth,SpawnDeltaX,SpawnDeltaY}) -self.SpawnVisible=true -local SpawnX=0 -local SpawnY=0 -local SpawnXIndex=0 -local SpawnYIndex=0 -for SpawnGroupID=1,self.SpawnMaxGroups do -self:T({SpawnX,SpawnY,SpawnXIndex,SpawnYIndex}) -self.SpawnGroups[SpawnGroupID].Visible=true -self.SpawnGroups[SpawnGroupID].Spawned=false -SpawnXIndex=SpawnXIndex+1 -if SpawnWidth and SpawnWidth~=0 then -if SpawnXIndex>=SpawnWidth then -SpawnXIndex=0 -SpawnYIndex=SpawnYIndex+1 -end -end -local SpawnRootX=self.SpawnGroups[SpawnGroupID].SpawnTemplate.x -local SpawnRootY=self.SpawnGroups[SpawnGroupID].SpawnTemplate.y -self:_TranslateRotate(SpawnGroupID,SpawnRootX,SpawnRootY,SpawnX,SpawnY,SpawnAngle) -self.SpawnGroups[SpawnGroupID].SpawnTemplate.lateActivation=true -self.SpawnGroups[SpawnGroupID].SpawnTemplate.visible=true -self.SpawnGroups[SpawnGroupID].Visible=true -self:HandleEvent(EVENTS.Birth,self._OnBirth) -self:HandleEvent(EVENTS.Dead,self._OnDeadOrCrash) -self:HandleEvent(EVENTS.Crash,self._OnDeadOrCrash) -self:HandleEvent(EVENTS.RemoveUnit,self._OnDeadOrCrash) -if self.Repeat then -self:HandleEvent(EVENTS.Takeoff,self._OnTakeOff) -self:HandleEvent(EVENTS.Land,self._OnLand) -end -if self.RepeatOnEngineShutDown then -self:HandleEvent(EVENTS.EngineShutdown,self._OnEngineShutDown) -end -self.SpawnGroups[SpawnGroupID].Group=_DATABASE:Spawn(self.SpawnGroups[SpawnGroupID].SpawnTemplate) -SpawnX=SpawnXIndex*SpawnDeltaX -SpawnY=SpawnYIndex*SpawnDeltaY -end -return self -end -do -function SPAWN:InitAIOnOff(AIOnOff) -self.AIOnOff=AIOnOff -return self -end -function SPAWN:InitAIOn() -return self:InitAIOnOff(true) -end -function SPAWN:InitAIOff() -return self:InitAIOnOff(false) -end -end -do -function SPAWN:InitDelayOnOff(DelayOnOff) -self.DelayOnOff=DelayOnOff -return self -end -function SPAWN:InitDelayOn() -return self:InitDelayOnOff(true) -end -function SPAWN:InitDelayOff() -return self:InitDelayOnOff(false) -end -end -function SPAWN:Spawn() -self:F({self.SpawnTemplatePrefix,self.SpawnIndex,self.AliveUnits}) -if self.SpawnInitAirbase then -return self:SpawnAtAirbase(self.SpawnInitAirbase,self.SpawnInitTakeoff,nil,self.SpawnInitTerminalType) -else -return self:SpawnWithIndex(self.SpawnIndex+1) -end -end -function SPAWN:ReSpawn(SpawnIndex) -self:F({self.SpawnTemplatePrefix,SpawnIndex}) -if not SpawnIndex then -SpawnIndex=1 -end -local SpawnGroup=self:GetGroupFromIndex(SpawnIndex) -local WayPoints=SpawnGroup and SpawnGroup.WayPoints or nil -if SpawnGroup then -local SpawnDCSGroup=SpawnGroup:GetDCSObject() -if SpawnDCSGroup then -SpawnGroup:Destroy() -end -end -local SpawnGroup=self:SpawnWithIndex(SpawnIndex) -if SpawnGroup and WayPoints then -SpawnGroup:WayPointInitialize(WayPoints) -SpawnGroup:WayPointExecute(1,5) -end -if SpawnGroup.ReSpawnFunction then -SpawnGroup:ReSpawnFunction() -end -SpawnGroup:ResetEvents() -return SpawnGroup -end -function SPAWN:SetSpawnIndex(SpawnIndex) -self.SpawnIndex=SpawnIndex or 0 -end -function SPAWN:SpawnWithIndex(SpawnIndex,NoBirth) -self:F2({SpawnTemplatePrefix=self.SpawnTemplatePrefix,SpawnIndex=SpawnIndex,AliveUnits=self.AliveUnits,SpawnMaxGroups=self.SpawnMaxGroups}) -if self:_GetSpawnIndex(SpawnIndex)then -if self.SpawnGroups[self.SpawnIndex].Visible then -self.SpawnGroups[self.SpawnIndex].Group:Activate() -else -local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate -self:T(SpawnTemplate.name) -if SpawnTemplate then -local PointVec3=POINT_VEC3:New(SpawnTemplate.route.points[1].x,SpawnTemplate.route.points[1].alt,SpawnTemplate.route.points[1].y) -self:T({"Current point of ",self.SpawnTemplatePrefix,PointVec3}) -if self.SpawnRandomizePosition then -local RandomVec2=PointVec3:GetRandomVec2InRadius(self.SpawnRandomizePositionOuterRadius,self.SpawnRandomizePositionInnerRadius) -local CurrentX=SpawnTemplate.units[1].x -local CurrentY=SpawnTemplate.units[1].y -SpawnTemplate.x=RandomVec2.x -SpawnTemplate.y=RandomVec2.y -for UnitID=1,#SpawnTemplate.units do -SpawnTemplate.units[UnitID].x=SpawnTemplate.units[UnitID].x+(RandomVec2.x-CurrentX) -SpawnTemplate.units[UnitID].y=SpawnTemplate.units[UnitID].y+(RandomVec2.y-CurrentY) -self:T('SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -end -end -if self.SpawnRandomizeUnits then -for UnitID=1,#SpawnTemplate.units do -local RandomVec2=PointVec3:GetRandomVec2InRadius(self.SpawnOuterRadius,self.SpawnInnerRadius) -SpawnTemplate.units[UnitID].x=RandomVec2.x -SpawnTemplate.units[UnitID].y=RandomVec2.y -self:T('SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -end -end -local function _Heading(courseDeg) -local h -if courseDeg<=180 then -h=math.rad(courseDeg) -else -h=-math.rad(360-courseDeg) -end -return h -end -local Rad180=math.rad(180) -local function _HeadingRad(courseRad) -if courseRad<=Rad180 then -return courseRad -else -return-((2*Rad180)-courseRad) -end -end -local function _RandomInRange(min,max) -if min and max then -return min+(math.random()*(max-min)) -else -return min -end -end -if self.SpawnInitGroupHeadingMin and#SpawnTemplate.units>0 then -local pivotX=SpawnTemplate.units[1].x -local pivotY=SpawnTemplate.units[1].y -local headingRad=math.rad(_RandomInRange(self.SpawnInitGroupHeadingMin or 0,self.SpawnInitGroupHeadingMax)) -local cosHeading=math.cos(headingRad) -local sinHeading=math.sin(headingRad) -local unitVarRad=math.rad(self.SpawnInitGroupUnitVar or 0) -for UnitID=1,#SpawnTemplate.units do -if UnitID>1 then -local unitXOff=SpawnTemplate.units[UnitID].x-pivotX -local unitYOff=SpawnTemplate.units[UnitID].y-pivotY -SpawnTemplate.units[UnitID].x=pivotX+(unitXOff*cosHeading)-(unitYOff*sinHeading) -SpawnTemplate.units[UnitID].y=pivotY+(unitYOff*cosHeading)+(unitXOff*sinHeading) -end -local unitHeading=SpawnTemplate.units[UnitID].heading+headingRad -SpawnTemplate.units[UnitID].heading=_HeadingRad(_RandomInRange(unitHeading-unitVarRad,unitHeading+unitVarRad)) -SpawnTemplate.units[UnitID].psi=-SpawnTemplate.units[UnitID].heading -end -end -if self.SpawnInitHeadingMin then -for UnitID=1,#SpawnTemplate.units do -SpawnTemplate.units[UnitID].heading=_Heading(_RandomInRange(self.SpawnInitHeadingMin,self.SpawnInitHeadingMax)) -SpawnTemplate.units[UnitID].psi=-SpawnTemplate.units[UnitID].heading -end -end -if self.SpawnInitLivery then -for UnitID=1,#SpawnTemplate.units do -SpawnTemplate.units[UnitID].livery_id=self.SpawnInitLivery -end -end -if self.SpawnInitSkill then -for UnitID=1,#SpawnTemplate.units do -SpawnTemplate.units[UnitID].skill=self.SpawnInitSkill -end -end -if self.SpawnInitModex then -for UnitID=1,#SpawnTemplate.units do -SpawnTemplate.units[UnitID].onboard_num=string.format("%03d",self.SpawnInitModex+(UnitID-1)) -end -end -if self.SpawnInitRadio then -SpawnTemplate.communication=self.SpawnInitRadio -end -if self.SpawnInitFreq then -SpawnTemplate.frequency=self.SpawnInitFreq -end -if self.SpawnInitModu then -SpawnTemplate.modulation=self.SpawnInitModu -end -SpawnTemplate.CategoryID=self.SpawnInitCategory or SpawnTemplate.CategoryID -SpawnTemplate.CountryID=self.SpawnInitCountry or SpawnTemplate.CountryID -SpawnTemplate.CoalitionID=self.SpawnInitCoalition or SpawnTemplate.CoalitionID -end -if not NoBirth then -self:HandleEvent(EVENTS.Birth,self._OnBirth) -end -self:HandleEvent(EVENTS.Dead,self._OnDeadOrCrash) -self:HandleEvent(EVENTS.Crash,self._OnDeadOrCrash) -self:HandleEvent(EVENTS.RemoveUnit,self._OnDeadOrCrash) -if self.Repeat then -self:HandleEvent(EVENTS.Takeoff,self._OnTakeOff) -self:HandleEvent(EVENTS.Land,self._OnLand) -end -if self.RepeatOnEngineShutDown then -self:HandleEvent(EVENTS.EngineShutdown,self._OnEngineShutDown) -end -self.SpawnGroups[self.SpawnIndex].Group=_DATABASE:Spawn(SpawnTemplate) -local SpawnGroup=self.SpawnGroups[self.SpawnIndex].Group -if SpawnGroup then -SpawnGroup:SetAIOnOff(self.AIOnOff) -end -self:T3(SpawnTemplate.name) -if self.SpawnFunctionHook then -self.SpawnHookScheduler:Schedule(nil,self.SpawnFunctionHook,{self.SpawnGroups[self.SpawnIndex].Group,unpack(self.SpawnFunctionArguments)},0.1) -end -end -self.SpawnGroups[self.SpawnIndex].Spawned=true -return self.SpawnGroups[self.SpawnIndex].Group -else -end -return nil -end -function SPAWN:SpawnScheduled(SpawnTime,SpawnTimeVariation) -self:F({SpawnTime,SpawnTimeVariation}) -if SpawnTime~=nil and SpawnTimeVariation~=nil then -local InitialDelay=0 -if self.DelayOnOff==true then -InitialDelay=math.random(SpawnTime-SpawnTime*SpawnTimeVariation,SpawnTime+SpawnTime*SpawnTimeVariation) -end -self.SpawnScheduler=SCHEDULER:New(self,self._Scheduler,{},InitialDelay,SpawnTime,SpawnTimeVariation) -end -return self -end -function SPAWN:SpawnScheduleStart() -self:F({self.SpawnTemplatePrefix}) -self.SpawnScheduler:Start() -return self -end -function SPAWN:SpawnScheduleStop() -self:F({self.SpawnTemplatePrefix}) -self.SpawnScheduler:Stop() -return self -end -function SPAWN:OnSpawnGroup(SpawnCallBackFunction,...) -self:F("OnSpawnGroup") -self.SpawnFunctionHook=SpawnCallBackFunction -self.SpawnFunctionArguments={} -if arg then -self.SpawnFunctionArguments=arg -end -return self -end -function SPAWN:SpawnAtAirbase(SpawnAirbase,Takeoff,TakeoffAltitude,TerminalType,EmergencyAirSpawn,Parkingdata) -self:F({self.SpawnTemplatePrefix,SpawnAirbase,Takeoff,TakeoffAltitude,TerminalType}) -local PointVec3=SpawnAirbase:GetCoordinate() -self:T2(PointVec3) -Takeoff=Takeoff or SPAWN.Takeoff.Hot -if EmergencyAirSpawn==nil then -EmergencyAirSpawn=true -end -self:F({SpawnIndex=self.SpawnIndex}) -if self:_GetSpawnIndex(self.SpawnIndex+1)then -local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate -self:F({SpawnTemplate=SpawnTemplate}) -if SpawnTemplate then -local GroupAlive=self:GetGroupFromIndex(self.SpawnIndex) -self:F({GroupAlive=GroupAlive}) -self:T({"Current point of ",self.SpawnTemplatePrefix,SpawnAirbase}) -local TemplateGroup=GROUP:FindByName(self.SpawnTemplatePrefix) -local TemplateUnit=TemplateGroup:GetUnit(1) -local group=TemplateGroup -local istransport=group:HasAttribute("Transports")and group:HasAttribute("Planes") -local isawacs=group:HasAttribute("AWACS") -local isfighter=group:HasAttribute("Fighters")or group:HasAttribute("Interceptors")or group:HasAttribute("Multirole fighters")or(group:HasAttribute("Bombers")and not group:HasAttribute("Strategic bombers")) -local isbomber=group:HasAttribute("Strategic bombers") -local istanker=group:HasAttribute("Tankers") -local ishelo=TemplateUnit:HasAttribute("Helicopters") -local nunits=#SpawnTemplate.units -local SpawnPoint=SpawnTemplate.route.points[1] -SpawnPoint.linkUnit=nil -SpawnPoint.helipadId=nil -SpawnPoint.airdromeId=nil -local AirbaseID=SpawnAirbase:GetID() -local AirbaseCategory=SpawnAirbase:GetAirbaseCategory() -self:F({AirbaseCategory=AirbaseCategory}) -if AirbaseCategory==Airbase.Category.SHIP then -SpawnPoint.linkUnit=AirbaseID -SpawnPoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.HELIPAD then -SpawnPoint.linkUnit=AirbaseID -SpawnPoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.AIRDROME then -SpawnPoint.airdromeId=AirbaseID -end -SpawnPoint.alt=0 -SpawnPoint.type=GROUPTEMPLATE.Takeoff[Takeoff][1] -SpawnPoint.action=GROUPTEMPLATE.Takeoff[Takeoff][2] -local spawnonground=not(Takeoff==SPAWN.Takeoff.Air) -self:T({spawnonground=spawnonground,TOtype=Takeoff,TOair=Takeoff==SPAWN.Takeoff.Air}) -local spawnonship=false -local spawnonfarp=false -local spawnonrunway=false -local spawnonairport=false -if spawnonground then -if AirbaseCategory==Airbase.Category.SHIP then -spawnonship=true -elseif AirbaseCategory==Airbase.Category.HELIPAD then -spawnonfarp=true -elseif AirbaseCategory==Airbase.Category.AIRDROME then -spawnonairport=true -end -spawnonrunway=Takeoff==SPAWN.Takeoff.Runway -end -local parkingspots={} -local parkingindex={} -local spots -if spawnonground and not SpawnTemplate.parked then -local nfree=0 -local termtype=TerminalType -if spawnonrunway then -if spawnonship then -if ishelo then -termtype=AIRBASE.TerminalType.HelicopterUsable -else -termtype=AIRBASE.TerminalType.OpenMedOrBig -end -else -termtype=AIRBASE.TerminalType.Runway -end -end -local scanradius=50 -local scanunits=true -local scanstatics=true -local scanscenery=false -local verysafe=false -if spawnonship or spawnonfarp or spawnonrunway then -self:T(string.format("Group %s is spawned on farp/ship/runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype,true) -spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype,true) -else -if ishelo then -if termtype==nil then -self:T(string.format("Helo group %s is at %s using terminal type %d.",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),AIRBASE.TerminalType.HelicopterOnly)) -spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup,AIRBASE.TerminalType.HelicopterOnly,scanradius,scanunits,scanstatics,scanscenery,verysafe,nunits,Parkingdata) -nfree=#spots -if nfree=1 then -for i=1,nunits do -table.insert(parkingspots,spots[1].Coordinate) -table.insert(parkingindex,spots[1].TerminalID) -end -PointVec3=spots[1].Coordinate -else -_notenough=true -end -elseif spawnonairport then -if nfree>=nunits then -for i=1,nunits do -table.insert(parkingspots,spots[i].Coordinate) -table.insert(parkingindex,spots[i].TerminalID) -end -else -_notenough=true -end -end -if _notenough then -if EmergencyAirSpawn and not self.SpawnUnControlled then -self:E(string.format("WARNING: Group %s has no parking spots at %s ==> air start!",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -spawnonground=false -spawnonship=false -spawnonfarp=false -spawnonrunway=false -SpawnPoint.type=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -SpawnPoint.action=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -PointVec3.x=PointVec3.x+math.random(-500,500) -PointVec3.z=PointVec3.z+math.random(-500,500) -if ishelo then -PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000) -else -PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500) -end -Takeoff=GROUP.Takeoff.Air -else -self:E(string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -return nil -end -end -else -if TakeoffAltitude then -PointVec3.y=TakeoffAltitude -else -if ishelo then -PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000) -else -PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500) -end -end -end -if not SpawnTemplate.parked then -SpawnTemplate.parked=true -for UnitID=1,nunits do -self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -local UnitTemplate=SpawnTemplate.units[UnitID] -local SX=UnitTemplate.x -local SY=UnitTemplate.y -local BX=SpawnTemplate.route.points[1].x -local BY=SpawnTemplate.route.points[1].y -local TX=PointVec3.x+(SX-BX) -local TY=PointVec3.z+(SY-BY) -if spawnonground then -if spawnonship or spawnonfarp or spawnonrunway then -self:T(string.format("Group %s spawning at farp, ship or runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -SpawnTemplate.units[UnitID].x=PointVec3.x -SpawnTemplate.units[UnitID].y=PointVec3.z -SpawnTemplate.units[UnitID].alt=PointVec3.y -else -self:T(string.format("Group %s spawning at airbase %s on parking spot id %d",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),parkingindex[UnitID])) -SpawnTemplate.units[UnitID].x=parkingspots[UnitID].x -SpawnTemplate.units[UnitID].y=parkingspots[UnitID].z -SpawnTemplate.units[UnitID].alt=parkingspots[UnitID].y -end -else -self:T(string.format("Group %s spawning in air at %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -SpawnTemplate.units[UnitID].x=TX -SpawnTemplate.units[UnitID].y=TY -SpawnTemplate.units[UnitID].alt=PointVec3.y -end -UnitTemplate.parking=nil -UnitTemplate.parking_id=nil -if parkingindex[UnitID]then -UnitTemplate.parking=parkingindex[UnitID] -end -self:T(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking))) -self:T(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking_id))) -self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -end -end -SpawnPoint.x=PointVec3.x -SpawnPoint.y=PointVec3.z -SpawnPoint.alt=PointVec3.y -SpawnTemplate.x=PointVec3.x -SpawnTemplate.y=PointVec3.z -SpawnTemplate.uncontrolled=self.SpawnUnControlled -local GroupSpawned=self:SpawnWithIndex(self.SpawnIndex) -if Takeoff==GROUP.Takeoff.Air then -for UnitID,UnitSpawned in pairs(GroupSpawned:GetUnits())do -SCHEDULER:New(nil,BASE.CreateEventTakeoff,{GroupSpawned,timer.getTime(),UnitSpawned:GetDCSObject()},5) -end -end -if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then -SCHEDULER:New(nil,AIRBASE.CheckOnRunWay,{SpawnAirbase,GroupSpawned,75,true},1.0) -end -return GroupSpawned -end -end -return nil -end -function SPAWN:SpawnAtParkingSpot(Airbase,Spots,Takeoff) -self:F({Airbase=Airbase,Spots=Spots,Takeoff=Takeoff}) -if type(Spots)~="table"then -Spots={Spots} -end -local group=GROUP:FindByName(self.SpawnTemplatePrefix) -local nunits=self.SpawnGrouping or#group:GetUnits() -if nunits then -if#Spots=nunits then -return self:SpawnAtAirbase(Airbase,Takeoff,nil,nil,nil,Parkingdata) -else -self:E("ERROR: Could not find enough free parking spots!") -end -else -self:E("ERROR: Could not get number of units in group!") -end -return nil -end -function SPAWN:ParkAircraft(SpawnAirbase,TerminalType,Parkingdata,SpawnIndex) -self:F({SpawnIndex=SpawnIndex,SpawnMaxGroups=self.SpawnMaxGroups}) -local PointVec3=SpawnAirbase:GetCoordinate() -self:T2(PointVec3) -local Takeoff=SPAWN.Takeoff.Cold -local SpawnTemplate=self.SpawnGroups[SpawnIndex].SpawnTemplate -if SpawnTemplate then -local GroupAlive=self:GetGroupFromIndex(SpawnIndex) -self:T({"Current point of ",self.SpawnTemplatePrefix,SpawnAirbase}) -local TemplateGroup=GROUP:FindByName(self.SpawnTemplatePrefix) -local TemplateUnit=TemplateGroup:GetUnit(1) -local ishelo=TemplateUnit:HasAttribute("Helicopters") -local isbomber=TemplateUnit:HasAttribute("Bombers") -local istransport=TemplateUnit:HasAttribute("Transports") -local isfighter=TemplateUnit:HasAttribute("Battleplanes") -local nunits=#SpawnTemplate.units -local SpawnPoint=SpawnTemplate.route.points[1] -SpawnPoint.linkUnit=nil -SpawnPoint.helipadId=nil -SpawnPoint.airdromeId=nil -local AirbaseID=SpawnAirbase:GetID() -local AirbaseCategory=SpawnAirbase:GetAirbaseCategory() -self:F({AirbaseCategory=AirbaseCategory}) -if AirbaseCategory==Airbase.Category.SHIP then -SpawnPoint.linkUnit=AirbaseID -SpawnPoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.HELIPAD then -SpawnPoint.linkUnit=AirbaseID -SpawnPoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.AIRDROME then -SpawnPoint.airdromeId=AirbaseID -end -SpawnPoint.alt=0 -SpawnPoint.type=GROUPTEMPLATE.Takeoff[Takeoff][1] -SpawnPoint.action=GROUPTEMPLATE.Takeoff[Takeoff][2] -local spawnonground=not(Takeoff==SPAWN.Takeoff.Air) -self:T({spawnonground=spawnonground,TOtype=Takeoff,TOair=Takeoff==SPAWN.Takeoff.Air}) -local spawnonship=false -local spawnonfarp=false -local spawnonrunway=false -local spawnonairport=false -if spawnonground then -if AirbaseCategory==Airbase.Category.SHIP then -spawnonship=true -elseif AirbaseCategory==Airbase.Category.HELIPAD then -spawnonfarp=true -elseif AirbaseCategory==Airbase.Category.AIRDROME then -spawnonairport=true -end -spawnonrunway=Takeoff==SPAWN.Takeoff.Runway -end -local parkingspots={} -local parkingindex={} -local spots -if spawnonground and not SpawnTemplate.parked then -local nfree=0 -local termtype=TerminalType -local scanradius=50 -local scanunits=true -local scanstatics=true -local scanscenery=false -local verysafe=false -if spawnonship or spawnonfarp or spawnonrunway then -self:T(string.format("Group %s is spawned on farp/ship/runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype,true) -spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype,true) -else -if ishelo then -if termtype==nil then -self:T(string.format("Helo group %s is at %s using terminal type %d.",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),AIRBASE.TerminalType.HelicopterOnly)) -spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup,AIRBASE.TerminalType.HelicopterOnly,scanradius,scanunits,scanstatics,scanscenery,verysafe,nunits,Parkingdata) -nfree=#spots -if nfree=1 then -for i=1,nunits do -table.insert(parkingspots,spots[1].Coordinate) -table.insert(parkingindex,spots[1].TerminalID) -end -PointVec3=spots[1].Coordinate -else -_notenough=true -end -elseif spawnonairport then -if nfree>=nunits then -for i=1,nunits do -table.insert(parkingspots,spots[i].Coordinate) -table.insert(parkingindex,spots[i].TerminalID) -end -else -_notenough=true -end -end -if _notenough then -if not self.SpawnUnControlled then -else -self:E(string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -return nil -end -end -else -end -if not SpawnTemplate.parked then -SpawnTemplate.parked=true -for UnitID=1,nunits do -self:F('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -local UnitTemplate=SpawnTemplate.units[UnitID] -local SX=UnitTemplate.x -local SY=UnitTemplate.y -local BX=SpawnTemplate.route.points[1].x -local BY=SpawnTemplate.route.points[1].y -local TX=PointVec3.x+(SX-BX) -local TY=PointVec3.z+(SY-BY) -if spawnonground then -if spawnonship or spawnonfarp or spawnonrunway then -self:T(string.format("Group %s spawning at farp, ship or runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -SpawnTemplate.units[UnitID].x=PointVec3.x -SpawnTemplate.units[UnitID].y=PointVec3.z -SpawnTemplate.units[UnitID].alt=PointVec3.y -else -self:T(string.format("Group %s spawning at airbase %s on parking spot id %d",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),parkingindex[UnitID])) -SpawnTemplate.units[UnitID].x=parkingspots[UnitID].x -SpawnTemplate.units[UnitID].y=parkingspots[UnitID].z -SpawnTemplate.units[UnitID].alt=parkingspots[UnitID].y -end -else -self:T(string.format("Group %s spawning in air at %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName())) -SpawnTemplate.units[UnitID].x=TX -SpawnTemplate.units[UnitID].y=TY -SpawnTemplate.units[UnitID].alt=PointVec3.y -end -UnitTemplate.parking=nil -UnitTemplate.parking_id=nil -if parkingindex[UnitID]then -UnitTemplate.parking=parkingindex[UnitID] -end -self:T2(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking))) -self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking_id))) -self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -end -end -SpawnPoint.x=PointVec3.x -SpawnPoint.y=PointVec3.z -SpawnPoint.alt=PointVec3.y -SpawnTemplate.x=PointVec3.x -SpawnTemplate.y=PointVec3.z -SpawnTemplate.uncontrolled=true -local GroupSpawned=self:SpawnWithIndex(SpawnIndex,true) -if Takeoff==GROUP.Takeoff.Air then -for UnitID,UnitSpawned in pairs(GroupSpawned:GetUnits())do -SCHEDULER:New(nil,BASE.CreateEventTakeoff,{GroupSpawned,timer.getTime(),UnitSpawned:GetDCSObject()},5) -end -end -if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then -SCHEDULER:New(nil,AIRBASE.CheckOnRunWay,{SpawnAirbase,GroupSpawned,75,true},1.0) -end -end -end -function SPAWN:ParkAtAirbase(SpawnAirbase,TerminalType,Parkingdata) -self:F({self.SpawnTemplatePrefix,SpawnAirbase,TerminalType}) -self:ParkAircraft(SpawnAirbase,TerminalType,Parkingdata,1) -for SpawnIndex=2,self.SpawnMaxGroups do -self:ParkAircraft(SpawnAirbase,TerminalType,Parkingdata,SpawnIndex) -end -self:SetSpawnIndex(0) -return nil -end -function SPAWN:SpawnFromVec3(Vec3,SpawnIndex) -self:F({self.SpawnTemplatePrefix,Vec3,SpawnIndex}) -local PointVec3=POINT_VEC3:NewFromVec3(Vec3) -self:T2(PointVec3) -if SpawnIndex then -else -SpawnIndex=self.SpawnIndex+1 -end -if self:_GetSpawnIndex(SpawnIndex)then -local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate -if SpawnTemplate then -self:T({"Current point of ",self.SpawnTemplatePrefix,Vec3}) -local TemplateHeight=SpawnTemplate.route and SpawnTemplate.route.points[1].alt or nil -SpawnTemplate.route=SpawnTemplate.route or{} -SpawnTemplate.route.points=SpawnTemplate.route.points or{} -SpawnTemplate.route.points[1]=SpawnTemplate.route.points[1]or{} -SpawnTemplate.route.points[1].x=SpawnTemplate.route.points[1].x or 0 -SpawnTemplate.route.points[1].y=SpawnTemplate.route.points[1].y or 0 -for UnitID=1,#SpawnTemplate.units do -local UnitTemplate=SpawnTemplate.units[UnitID] -local SX=UnitTemplate.x or 0 -local SY=UnitTemplate.y or 0 -local BX=SpawnTemplate.route.points[1].x -local BY=SpawnTemplate.route.points[1].y -local TX=Vec3.x+(SX-BX) -local TY=Vec3.z+(SY-BY) -SpawnTemplate.units[UnitID].x=TX -SpawnTemplate.units[UnitID].y=TY -if SpawnTemplate.CategoryID~=Group.Category.SHIP then -SpawnTemplate.units[UnitID].alt=Vec3.y or TemplateHeight -end -self:T('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -end -SpawnTemplate.route.points[1].x=Vec3.x -SpawnTemplate.route.points[1].y=Vec3.z -if SpawnTemplate.CategoryID~=Group.Category.SHIP then -SpawnTemplate.route.points[1].alt=Vec3.y or TemplateHeight -end -SpawnTemplate.x=Vec3.x -SpawnTemplate.y=Vec3.z -SpawnTemplate.alt=Vec3.y or TemplateHeight -return self:SpawnWithIndex(self.SpawnIndex) -end -end -return nil -end -function SPAWN:SpawnFromCoordinate(Coordinate,SpawnIndex) -self:F({self.SpawnTemplatePrefix,SpawnIndex}) -return self:SpawnFromVec3(Coordinate:GetVec3(),SpawnIndex) -end -function SPAWN:SpawnFromPointVec3(PointVec3,SpawnIndex) -self:F({self.SpawnTemplatePrefix,SpawnIndex}) -return self:SpawnFromVec3(PointVec3:GetVec3(),SpawnIndex) -end -function SPAWN:SpawnFromVec2(Vec2,MinHeight,MaxHeight,SpawnIndex) -self:F({self.SpawnTemplatePrefix,self.SpawnIndex,Vec2,MinHeight,MaxHeight,SpawnIndex}) -local Height=nil -if MinHeight and MaxHeight then -Height=math.random(MinHeight,MaxHeight) -end -return self:SpawnFromVec3({x=Vec2.x,y=Height,z=Vec2.y},SpawnIndex) -end -function SPAWN:SpawnFromPointVec2(PointVec2,MinHeight,MaxHeight,SpawnIndex) -self:F({self.SpawnTemplatePrefix,self.SpawnIndex}) -return self:SpawnFromVec2(PointVec2:GetVec2(),MinHeight,MaxHeight,SpawnIndex) -end -function SPAWN:SpawnFromUnit(HostUnit,MinHeight,MaxHeight,SpawnIndex) -self:F({self.SpawnTemplatePrefix,HostUnit,MinHeight,MaxHeight,SpawnIndex}) -if HostUnit and HostUnit:IsAlive()~=nil then -return self:SpawnFromVec2(HostUnit:GetVec2(),MinHeight,MaxHeight,SpawnIndex) -end -return nil -end -function SPAWN:SpawnFromStatic(HostStatic,MinHeight,MaxHeight,SpawnIndex) -self:F({self.SpawnTemplatePrefix,HostStatic,MinHeight,MaxHeight,SpawnIndex}) -if HostStatic and HostStatic:IsAlive()then -return self:SpawnFromVec2(HostStatic:GetVec2(),MinHeight,MaxHeight,SpawnIndex) -end -return nil -end -function SPAWN:SpawnInZone(Zone,RandomizeGroup,MinHeight,MaxHeight,SpawnIndex) -self:F({self.SpawnTemplatePrefix,Zone,RandomizeGroup,MinHeight,MaxHeight,SpawnIndex}) -if Zone then -if RandomizeGroup then -return self:SpawnFromVec2(Zone:GetRandomVec2(),MinHeight,MaxHeight,SpawnIndex) -else -return self:SpawnFromVec2(Zone:GetVec2(),MinHeight,MaxHeight,SpawnIndex) -end -end -return nil -end -function SPAWN:InitUnControlled(UnControlled) -self:F2({self.SpawnTemplatePrefix,UnControlled}) -self.SpawnUnControlled=(UnControlled==true)and true or nil -for SpawnGroupID=1,self.SpawnMaxGroups do -self.SpawnGroups[SpawnGroupID].UnControlled=self.SpawnUnControlled -end -return self -end -function SPAWN:GetCoordinate() -local LateGroup=GROUP:FindByName(self.SpawnTemplatePrefix) -if LateGroup then -return LateGroup:GetCoordinate() -end -return nil -end -function SPAWN:SpawnGroupName(SpawnIndex) -self:F({self.SpawnTemplatePrefix,SpawnIndex}) -local SpawnPrefix=self.SpawnTemplatePrefix -if self.SpawnAliasPrefix then -SpawnPrefix=self.SpawnAliasPrefix -end -if SpawnIndex then -local SpawnName=string.format('%s#%03d',SpawnPrefix,SpawnIndex) -self:T(SpawnName) -return SpawnName -else -self:T(SpawnPrefix) -return SpawnPrefix -end -end -function SPAWN:GetFirstAliveGroup() -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix}) -for SpawnIndex=1,self.SpawnCount do -local SpawnGroup=self:GetGroupFromIndex(SpawnIndex) -if SpawnGroup and SpawnGroup:IsAlive()then -return SpawnGroup,SpawnIndex -end -end -return nil,nil -end -function SPAWN:GetNextAliveGroup(SpawnIndexStart) -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnIndexStart}) -SpawnIndexStart=SpawnIndexStart+1 -for SpawnIndex=SpawnIndexStart,self.SpawnCount do -local SpawnGroup=self:GetGroupFromIndex(SpawnIndex) -if SpawnGroup and SpawnGroup:IsAlive()then -return SpawnGroup,SpawnIndex -end -end -return nil,nil -end -function SPAWN:GetLastAliveGroup() -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix}) -for SpawnIndex=self.SpawnCount,1,-1 do -local SpawnGroup=self:GetGroupFromIndex(SpawnIndex) -if SpawnGroup and SpawnGroup:IsAlive()then -self.SpawnIndex=SpawnIndex -return SpawnGroup -end -end -self.SpawnIndex=nil -return nil -end -function SPAWN:GetGroupFromIndex(SpawnIndex) -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnIndex}) -if not SpawnIndex then -SpawnIndex=1 -end -if self.SpawnGroups and self.SpawnGroups[SpawnIndex]then -local SpawnGroup=self.SpawnGroups[SpawnIndex].Group -return SpawnGroup -else -return nil -end -end -function SPAWN:_GetPrefixFromGroup(SpawnGroup) -self:F3({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnGroup}) -local GroupName=SpawnGroup:GetName() -if GroupName then -local SpawnPrefix=string.match(GroupName,".*#") -if SpawnPrefix then -SpawnPrefix=SpawnPrefix:sub(1,-2) -end -return SpawnPrefix -end -return nil -end -function SPAWN:GetSpawnIndexFromGroup(SpawnGroup) -self:F2({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnGroup}) -local IndexString=string.match(SpawnGroup:GetName(),"#(%d*)$"):sub(2) -local Index=tonumber(IndexString) -self:T3(IndexString,Index) -return Index -end -function SPAWN:_GetLastIndex() -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix}) -return self.SpawnMaxGroups -end -function SPAWN:_InitializeSpawnGroups(SpawnIndex) -self:F3({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnIndex}) -if not self.SpawnGroups[SpawnIndex]then -self.SpawnGroups[SpawnIndex]={} -self.SpawnGroups[SpawnIndex].Visible=false -self.SpawnGroups[SpawnIndex].Spawned=false -self.SpawnGroups[SpawnIndex].UnControlled=false -self.SpawnGroups[SpawnIndex].SpawnTime=0 -self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix=self.SpawnTemplatePrefix -self.SpawnGroups[SpawnIndex].SpawnTemplate=self:_Prepare(self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix,SpawnIndex) -end -self:_RandomizeTemplate(SpawnIndex) -self:_RandomizeRoute(SpawnIndex) -return self.SpawnGroups[SpawnIndex] -end -function SPAWN:_GetGroupCategoryID(SpawnPrefix) -local TemplateGroup=Group.getByName(SpawnPrefix) -if TemplateGroup then -return TemplateGroup:getCategory() -else -return nil -end -end -function SPAWN:_GetGroupCoalitionID(SpawnPrefix) -local TemplateGroup=Group.getByName(SpawnPrefix) -if TemplateGroup then -return TemplateGroup:getCoalition() -else -return nil -end -end -function SPAWN:_GetGroupCountryID(SpawnPrefix) -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnPrefix}) -local TemplateGroup=Group.getByName(SpawnPrefix) -if TemplateGroup then -local TemplateUnits=TemplateGroup:getUnits() -return TemplateUnits[1]:getCountry() -else -return nil -end -end -function SPAWN:_GetTemplate(SpawnTemplatePrefix) -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnTemplatePrefix}) -local SpawnTemplate=nil -local Template=_DATABASE.Templates.Groups[SpawnTemplatePrefix].Template -self:F({Template=Template}) -SpawnTemplate=UTILS.DeepCopy(_DATABASE.Templates.Groups[SpawnTemplatePrefix].Template) -if SpawnTemplate==nil then -error('No Template returned for SpawnTemplatePrefix = '..SpawnTemplatePrefix) -end -self:T3({SpawnTemplate}) -return SpawnTemplate -end -function SPAWN:_Prepare(SpawnTemplatePrefix,SpawnIndex) -self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix}) -local SpawnTemplate -if self.TweakedTemplate~=nil and self.TweakedTemplate==true then -BASE:I("WARNING: You are using a tweaked template.") -SpawnTemplate=self.SpawnTemplate -else -SpawnTemplate=self:_GetTemplate(SpawnTemplatePrefix) -SpawnTemplate.name=self:SpawnGroupName(SpawnIndex) -end -SpawnTemplate.groupId=nil -SpawnTemplate.lateActivation=self.LateActivated or false -if SpawnTemplate.CategoryID==Group.Category.GROUND then -self:T3("For ground units, visible needs to be false...") -SpawnTemplate.visible=false -end -if self.SpawnGrouping then -local UnitAmount=#SpawnTemplate.units -self:F({UnitAmount=UnitAmount,SpawnGrouping=self.SpawnGrouping}) -if UnitAmount>self.SpawnGrouping then -for UnitID=self.SpawnGrouping+1,UnitAmount do -SpawnTemplate.units[UnitID]=nil -end -else -if UnitAmount0 then -self.Tstop=timer.getTime()+Delay -else -if self.tid then -self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!",self.ncalls)) -timer.removeFunction(self.tid) -self.isrunning=false -end -end -return self -end -function TIMER:SetMaxFunctionCalls(Nmax) -self.ncallsMax=Nmax -return self -end -function TIMER:IsRunning() -return self.isrunning -end -function TIMER:_Function(time) -self.func(unpack(self.para)) -self.ncalls=self.ncalls+1 -local Tnext=self.dT and time+self.dT or nil -local stopme=false -if Tnext==nil then -self:T(self.lid..string.format("No next time as dT=nil ==> Stopping timer after %d function calls",self.ncalls)) -stopme=true -elseif self.Tstop and Tnext>self.Tstop then -self:T(self.lid..string.format("Stop time passed %.3f > %.3f ==> Stopping timer after %d function calls",Tnext,self.Tstop,self.ncalls)) -stopme=true -elseif self.ncallsMax and self.ncalls>=self.ncallsMax then -self:T(self.lid..string.format("Max function calls Nmax=%d reached ==> Stopping timer after %d function calls",self.ncallsMax,self.ncalls)) -stopme=true -end -if stopme then -self:Stop() -return nil -else -return Tnext -end -end -do -GOAL={ -ClassName="GOAL", -} -GOAL.Players={} -GOAL.TotalContributions=0 -function GOAL:New() -local self=BASE:Inherit(self,FSM:New()) -self:F({}) -self:SetStartState("Pending") -self:AddTransition("*","Achieved","Achieved") -self:SetEventPriority(5) -return self -end -function GOAL:AddPlayerContribution(PlayerName) -self:F({PlayerName}) -self.Players[PlayerName]=self.Players[PlayerName]or 0 -self.Players[PlayerName]=self.Players[PlayerName]+1 -self.TotalContributions=self.TotalContributions+1 -end -function GOAL:GetPlayerContribution(PlayerName) -return self.Players[PlayerName]or 0 -end -function GOAL:GetPlayerContributions() -return self.Players or{} -end -function GOAL:GetTotalContributions() -return self.TotalContributions or 0 -end -function GOAL:IsAchieved() -return self:Is("Achieved") -end -end -do -SPOT={ -ClassName="SPOT", -} -function SPOT:New(Recce) -local self=BASE:Inherit(self,FSM:New()) -self:F({}) -self:SetStartState("Off") -self:AddTransition("Off","LaseOn","On") -self:AddTransition("Off","LaseOnCoordinate","On") -self:AddTransition("On","Lasing","On") -self:AddTransition({"On","Destroyed"},"LaseOff","Off") -self:AddTransition("*","Destroyed","Destroyed") -self.Recce=Recce -self.LaseScheduler=SCHEDULER:New(self) -self:SetEventPriority(5) -self.Lasing=false -return self -end -function SPOT:onafterLaseOn(From,Event,To,Target,LaserCode,Duration) -self:F({"LaseOn",Target,LaserCode,Duration}) -local function StopLase(self) -self:LaseOff() -end -self.Target=Target -self.LaserCode=LaserCode -self.Lasing=true -local RecceDcsUnit=self.Recce:GetDCSObject() -self.SpotIR=Spot.createInfraRed(RecceDcsUnit,{x=0,y=2,z=0},Target:GetPointVec3():AddY(1):GetVec3()) -self.SpotLaser=Spot.createLaser(RecceDcsUnit,{x=0,y=2,z=0},Target:GetPointVec3():AddY(1):GetVec3(),LaserCode) -if Duration then -self.ScheduleID=self.LaseScheduler:Schedule(self,StopLase,{self},Duration) -end -self:HandleEvent(EVENTS.Dead) -self:__Lasing(-1) -end -function SPOT:onafterLaseOnCoordinate(From,Event,To,Coordinate,LaserCode,Duration) -self:F({"LaseOnCoordinate",Coordinate,LaserCode,Duration}) -local function StopLase(self) -self:LaseOff() -end -self.Target=nil -self.TargetCoord=Coordinate -self.LaserCode=LaserCode -self.Lasing=true -local RecceDcsUnit=self.Recce:GetDCSObject() -self.SpotIR=Spot.createInfraRed(RecceDcsUnit,{x=0,y=1,z=0},Coordinate:GetVec3()) -self.SpotLaser=Spot.createLaser(RecceDcsUnit,{x=0,y=1,z=0},Coordinate:GetVec3(),LaserCode) -if Duration then -self.ScheduleID=self.LaseScheduler:Schedule(self,StopLase,{self},Duration) -end -self:__Lasing(-1) -end -function SPOT:OnEventDead(EventData) -self:F({Dead=EventData.IniDCSUnitName,Target=self.Target}) -if self.Target then -if EventData.IniDCSUnitName==self.Target:GetName()then -self:F({"Target dead ",self.Target:GetName()}) -self:Destroyed() -self:LaseOff() -end -end -end -function SPOT:onafterLasing(From,Event,To) -if self.Target and self.Target:IsAlive()then -self.SpotIR:setPoint(self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3()) -self.SpotLaser:setPoint(self.Target:GetPointVec3():AddY(1):GetVec3()) -self:__Lasing(-0.2) -elseif self.TargetCoord then -local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100,y=self.TargetCoord.y+math.random(-100,100)/100,z=self.TargetCoord.z} -local lsvec3={x=self.TargetCoord.x,y=self.TargetCoord.y,z=self.TargetCoord.z} -self.SpotIR:setPoint(irvec3) -self.SpotLaser:setPoint(lsvec3) -self:__Lasing(-0.25) -else -self:F({"Target is not alive",self.Target:IsAlive()}) -end -end -function SPOT:onafterLaseOff(From,Event,To) -self:F({"Stopped lasing for ",self.Target and self.Target:GetName()or"coord",SpotIR=self.SportIR,SpotLaser=self.SpotLaser}) -self.Lasing=false -self.SpotIR:destroy() -self.SpotLaser:destroy() -self.SpotIR=nil -self.SpotLaser=nil -if self.ScheduleID then -self.LaseScheduler:Stop(self.ScheduleID) -end -self.ScheduleID=nil -self.Target=nil -return self -end -function SPOT:IsLasing() -return self.Lasing -end -end -ASTAR={ -ClassName="ASTAR", -Debug=nil, -lid=nil, -nodes={}, -counter=1, -Nnodes=0, -ncost=0, -ncostcache=0, -nvalid=0, -nvalidcache=0, -} -ASTAR.INF=1/0 -ASTAR.version="0.4.0" -function ASTAR:New() -local self=BASE:Inherit(self,BASE:New()) -self.lid="ASTAR | " -return self -end -function ASTAR:SetStartCoordinate(Coordinate) -self.startCoord=Coordinate -return self -end -function ASTAR:SetEndCoordinate(Coordinate) -self.endCoord=Coordinate -return self -end -function ASTAR:GetNodeFromCoordinate(Coordinate) -local node={} -node.coordinate=Coordinate -node.surfacetype=Coordinate:GetSurfaceType() -node.id=self.counter -node.valid={} -node.cost={} -self.counter=self.counter+1 -return node -end -function ASTAR:AddNode(Node) -self.nodes[Node.id]=Node -self.Nnodes=self.Nnodes+1 -return self -end -function ASTAR:AddNodeFromCoordinate(Coordinate) -local node=self:GetNodeFromCoordinate(Coordinate) -self:AddNode(node) -return node -end -function ASTAR:CheckValidSurfaceType(Node,SurfaceTypes) -if SurfaceTypes then -if type(SurfaceTypes)~="table"then -SurfaceTypes={SurfaceTypes} -end -for _,surface in pairs(SurfaceTypes)do -if surface==Node.surfacetype then -return true -end -end -return false -else -return true -end -end -function ASTAR:SetValidNeighbourFunction(NeighbourFunction,...) -self.ValidNeighbourFunc=NeighbourFunction -self.ValidNeighbourArg={} -if arg then -self.ValidNeighbourArg=arg -end -return self -end -function ASTAR:SetValidNeighbourLoS(CorridorWidth) -self:SetValidNeighbourFunction(ASTAR.LoS,CorridorWidth) -return self -end -function ASTAR:SetValidNeighbourDistance(MaxDistance) -self:SetValidNeighbourFunction(ASTAR.DistMax,MaxDistance) -return self -end -function ASTAR:SetValidNeighbourRoad(MaxDistance) -self:SetValidNeighbourFunction(ASTAR.Road,MaxDistance) -return self -end -function ASTAR:SetCostFunction(CostFunction,...) -self.CostFunc=CostFunction -self.CostArg={} -if arg then -self.CostArg=arg -end -return self -end -function ASTAR:SetCostDist2D() -self:SetCostFunction(ASTAR.Dist2D) -return self -end -function ASTAR:SetCostDist3D() -self:SetCostFunction(ASTAR.Dist3D) -return self -end -function ASTAR:SetCostRoad() -self:SetCostFunction(ASTAR) -return self -end -function ASTAR:CreateGrid(ValidSurfaceTypes,BoxHY,SpaceX,deltaX,deltaY,MarkGrid) -local Dz=SpaceX or 10000 -local Dx=BoxHY and BoxHY/2 or 20000 -local dz=deltaX or 2000 -local dx=deltaY or dz -local angle=self.startCoord:HeadingTo(self.endCoord) -local dist=self.startCoord:Get2DDistance(self.endCoord)+2*Dz -local co=COORDINATE:New(0,0,0) -local do1=co:Get2DDistance(self.startCoord) -local ho1=co:HeadingTo(self.startCoord) -local xmin=-Dx -local zmin=-Dz -local nz=dist/dz+1 -local nx=2*Dx/dx+1 -local text=string.format("Building grid with nx=%d ny=%d => total=%d nodes",nx,nz,nx*nz) -self:T(self.lid..text) -for i=1,nx do -local x=xmin+dx*(i-1) -for j=1,nz do -local z=zmin+dz*(j-1) -local vec3=UTILS.Rotate2D({x=x,y=0,z=z},angle) -local c=COORDINATE:New(vec3.z,vec3.y,vec3.x):Translate(do1,ho1,true) -local node=self:GetNodeFromCoordinate(c) -if self:CheckValidSurfaceType(node,ValidSurfaceTypes)then -if MarkGrid then -c:MarkToAll(string.format("i=%d, j=%d surface=%d",i,j,node.surfacetype)) -end -self:AddNode(node) -end -end -end -local text=string.format("Done building grid!") -self:T2(self.lid..text) -return self -end -function ASTAR.LoS(nodeA,nodeB,corridor) -local offset=1 -local dx=corridor and corridor/2 or nil -local dy=dx -local cA=nodeA.coordinate:GetVec3() -local cB=nodeB.coordinate:GetVec3() -cA.y=offset -cB.y=offset -local los=land.isVisible(cA,cB) -if los and corridor then -local heading=nodeA.coordinate:HeadingTo(nodeB.coordinate) -local Ap=UTILS.VecTranslate(cA,dx,heading+90) -local Bp=UTILS.VecTranslate(cB,dx,heading+90) -los=land.isVisible(Ap,Bp) -if los then -local Am=UTILS.VecTranslate(cA,dx,heading-90) -local Bm=UTILS.VecTranslate(cB,dx,heading-90) -los=land.isVisible(Am,Bm) -end -end -return los -end -function ASTAR.Road(nodeA,nodeB) -local path=land.findPathOnRoads("roads",nodeA.coordinate.x,nodeA.coordinate.z,nodeB.coordinate.x,nodeB.coordinate.z) -if path then -return true -else -return false -end -end -function ASTAR.DistMax(nodeA,nodeB,distmax) -distmax=distmax or 2000 -local dist=nodeA.coordinate:Get2DDistance(nodeB.coordinate) -return dist<=distmax -end -function ASTAR.Dist2D(nodeA,nodeB) -local dist=nodeA.coordinate:Get2DDistance(nodeB) -return dist -end -function ASTAR.Dist3D(nodeA,nodeB) -local dist=nodeA.coordinate:Get3DDistance(nodeB.coordinate) -return dist -end -function ASTAR.DistRoad(nodeA,nodeB) -local path=land.findPathOnRoads("roads",nodeA.coordinate.x,nodeA.coordinate.z,nodeB.coordinate.x,nodeB.coordinate.z) -if path then -local dist=0 -for i=2,#path do -local b=path[i] -local a=path[i-1] -dist=dist+UTILS.VecDist2D(a,b) -end -return dist -end -return math.huge -end -function ASTAR:FindClosestNode(Coordinate) -local distMin=math.huge -local closeNode=nil -for _,_node in pairs(self.nodes)do -local node=_node -local dist=node.coordinate:Get2DDistance(Coordinate) -if dist1000 then -self:T(self.lid.."Adding start node to node grid!") -self:AddNode(node) -end -return self -end -function ASTAR:FindEndNode() -local node,dist=self:FindClosestNode(self.endCoord) -self.endNode=node -if dist>1000 then -self:T(self.lid.."Adding end node to node grid!") -self:AddNode(node) -end -return self -end -function ASTAR:GetPath(ExcludeStartNode,ExcludeEndNode) -self:FindStartNode() -self:FindEndNode() -local nodes=self.nodes -local start=self.startNode -local goal=self.endNode -local openset={} -local closedset={} -local came_from={} -local g_score={} -local f_score={} -openset[start.id]=true -local Nopen=1 -g_score[start.id]=0 -f_score[start.id]=g_score[start.id]+self:_HeuristicCost(start,goal) -local T0=timer.getAbsTime() -local text=string.format("Starting A* pathfinding with %d Nodes",self.Nnodes) -self:T(self.lid..text) -local Tstart=UTILS.GetOSTime() -while Nopen>0 do -local current=self:_LowestFscore(openset,f_score) -if current.id==goal.id then -local path=self:_UnwindPath({},came_from,goal) -if not ExcludeEndNode then -table.insert(path,goal) -end -if ExcludeStartNode then -table.remove(path,1) -end -local Tstop=UTILS.GetOSTime() -local dT=nil -if Tstart and Tstop then -dT=Tstop-Tstart -end -local text=string.format("Found path with %d nodes (%d total)",#path,self.Nnodes) -if dT then -text=text..string.format(", OS Time %.6f sec",dT) -end -text=text..string.format(", Nvalid=%d [%d cached]",self.nvalid,self.nvalidcache) -text=text..string.format(", Ncost=%d [%d cached]",self.ncost,self.ncostcache) -self:T(self.lid..text) -return path -end -openset[current.id]=nil -Nopen=Nopen-1 -closedset[current.id]=true -local neighbors=self:_NeighbourNodes(current,nodes) -for _,neighbor in pairs(neighbors)do -if self:_NotIn(closedset,neighbor.id)then -local tentative_g_score=g_score[current.id]+self:_DistNodes(current,neighbor) -if self:_NotIn(openset,neighbor.id)or tentative_g_score0 then -AoA=-AoA -end -return math.deg(AoA) -end -end -return nil -end -function POSITIONABLE:GetClimbAngle() -local unitpos=self:GetPosition() -if unitpos then -local unitvel=self:GetVelocityVec3() -if unitvel and UTILS.VecNorm(unitvel)~=0 then -local angle=math.asin(unitvel.y/UTILS.VecNorm(unitvel)) -return math.deg(angle) -else -return 0 -end -end -return nil -end -function POSITIONABLE:GetPitch() -local unitpos=self:GetPosition() -if unitpos then -return math.deg(math.asin(unitpos.x.y)) -end -return nil -end -function POSITIONABLE:GetRoll() -local unitpos=self:GetPosition() -if unitpos then -local cp=UTILS.VecCross(unitpos.x,{x=0,y=1,z=0}) -local dp=UTILS.VecDot(cp,unitpos.z) -local Roll=math.acos(dp/(UTILS.VecNorm(cp)*UTILS.VecNorm(unitpos.z))) -if unitpos.z.y>0 then -Roll=-Roll -end -return math.deg(Roll) -end -end -function POSITIONABLE:GetYaw() -local unitpos=self:GetPosition() -if unitpos then -local unitvel=self:GetVelocityVec3() -if unitvel and UTILS.VecNorm(unitvel)~=0 then -local AxialVel={} -AxialVel.x=UTILS.VecDot(unitpos.x,unitvel) -AxialVel.y=UTILS.VecDot(unitpos.y,unitvel) -AxialVel.z=UTILS.VecDot(unitpos.z,unitvel) -local Yaw=math.acos(UTILS.VecDot({x=1,y=0,z=0},{x=AxialVel.x,y=0,z=AxialVel.z})/UTILS.VecNorm({x=AxialVel.x,y=0,z=AxialVel.z})) -if AxialVel.z>0 then -Yaw=-Yaw -end -return Yaw -end -end -end -function POSITIONABLE:GetMessageText(Message,Name) -local DCSObject=self:GetDCSObject() -if DCSObject then -local Callsign=string.format("%s",((Name~=""and Name)or self:GetCallsign()~=""and self:GetCallsign())or self:GetName()) -local MessageText=string.format("%s - %s",Callsign,Message) -return MessageText -end -return nil -end -function POSITIONABLE:GetMessage(Message,Duration,Name) -local DCSObject=self:GetDCSObject() -if DCSObject then -local MessageText=self:GetMessageText(Message,Name) -return MESSAGE:New(MessageText,Duration) -end -return nil -end -function POSITIONABLE:GetMessageType(Message,MessageType,Name) -local DCSObject=self:GetDCSObject() -if DCSObject then -local MessageText=self:GetMessageText(Message,Name) -return MESSAGE:NewType(MessageText,MessageType) -end -return nil -end -function POSITIONABLE:MessageToAll(Message,Duration,Name) -self:F2({Message,Duration}) -local DCSObject=self:GetDCSObject() -if DCSObject then -self:GetMessage(Message,Duration,Name):ToAll() -end -return nil -end -function POSITIONABLE:MessageToCoalition(Message,Duration,MessageCoalition,Name) -self:F2({Message,Duration}) -local Name=Name or"" -local DCSObject=self:GetDCSObject() -if DCSObject then -self:GetMessage(Message,Duration,Name):ToCoalition(MessageCoalition) -end -return nil -end -function POSITIONABLE:MessageTypeToCoalition(Message,MessageType,MessageCoalition,Name) -self:F2({Message,MessageType}) -local Name=Name or"" -local DCSObject=self:GetDCSObject() -if DCSObject then -self:GetMessageType(Message,MessageType,Name):ToCoalition(MessageCoalition) -end -return nil -end -function POSITIONABLE:MessageToRed(Message,Duration,Name) -self:F2({Message,Duration}) -local DCSObject=self:GetDCSObject() -if DCSObject then -self:GetMessage(Message,Duration,Name):ToRed() -end -return nil -end -function POSITIONABLE:MessageToBlue(Message,Duration,Name) -self:F2({Message,Duration}) -local DCSObject=self:GetDCSObject() -if DCSObject then -self:GetMessage(Message,Duration,Name):ToBlue() -end -return nil -end -function POSITIONABLE:MessageToClient(Message,Duration,Client,Name) -self:F2({Message,Duration}) -local DCSObject=self:GetDCSObject() -if DCSObject then -self:GetMessage(Message,Duration,Name):ToClient(Client) -end -return nil -end -function POSITIONABLE:MessageToGroup(Message,Duration,MessageGroup,Name) -self:F2({Message,Duration}) -local DCSObject=self:GetDCSObject() -if DCSObject then -if DCSObject:isExist()then -if MessageGroup:IsAlive()then -self:GetMessage(Message,Duration,Name):ToGroup(MessageGroup) -else -BASE:E({"Message not sent to Group; Group is not alive...",Message=Message,MessageGroup=MessageGroup}) -end -else -BASE:E({"Message not sent to Group; Positionable is not alive ...",Message=Message,Positionable=self,MessageGroup=MessageGroup}) -end -end -return nil -end -function POSITIONABLE:MessageTypeToGroup(Message,MessageType,MessageGroup,Name) -self:F2({Message,MessageType}) -local DCSObject=self:GetDCSObject() -if DCSObject then -if DCSObject:isExist()then -self:GetMessageType(Message,MessageType,Name):ToGroup(MessageGroup) -end -end -return nil -end -function POSITIONABLE:MessageToSetGroup(Message,Duration,MessageSetGroup,Name) -self:F2({Message,Duration}) -local DCSObject=self:GetDCSObject() -if DCSObject then -if DCSObject:isExist()then -MessageSetGroup:ForEachGroupAlive( -function(MessageGroup) -self:GetMessage(Message,Duration,Name):ToGroup(MessageGroup) -end -) -end -end -return nil -end -function POSITIONABLE:Message(Message,Duration,Name) -self:F2({Message,Duration}) -local DCSObject=self:GetDCSObject() -if DCSObject then -self:GetMessage(Message,Duration,Name):ToGroup(self) -end -return nil -end -function POSITIONABLE:GetRadio() -self:F2(self) -return RADIO:New(self) -end -function POSITIONABLE:GetBeacon() -self:F2(self) -return BEACON:New(self) -end -function POSITIONABLE:LaseUnit(Target,LaserCode,Duration) -self:F2() -LaserCode=LaserCode or math.random(1000,9999) -local RecceDcsUnit=self:GetDCSObject() -local TargetVec3=Target:GetVec3() -self:F("bulding spot") -self.Spot=SPOT:New(self) -self.Spot:LaseOn(Target,LaserCode,Duration) -self.LaserCode=LaserCode -return self.Spot -end -function POSITIONABLE:LaseCoordinate(Coordinate,LaserCode,Duration) -self:F2() -LaserCode=LaserCode or math.random(1000,9999) -self.Spot=SPOT:New(self) -self.Spot:LaseOnCoordinate(Coordinate,LaserCode,Duration) -self.LaserCode=LaserCode -return self.Spot -end -function POSITIONABLE:LaseOff() -self:F2() -if self.Spot then -self.Spot:LaseOff() -self.Spot=nil -end -return self -end -function POSITIONABLE:IsLasing() -self:F2() -local Lasing=false -if self.Spot then -Lasing=self.Spot:IsLasing() -end -return Lasing -end -function POSITIONABLE:GetSpot() -return self.Spot -end -function POSITIONABLE:GetLaserCode() -return self.LaserCode -end -do -function POSITIONABLE:AddCargo(Cargo) -self.__.Cargo[Cargo]=Cargo -return self -end -function POSITIONABLE:GetCargo() -return self.__.Cargo -end -function POSITIONABLE:RemoveCargo(Cargo) -self.__.Cargo[Cargo]=nil -return self -end -function POSITIONABLE:HasCargo(Cargo) -return self.__.Cargo[Cargo] -end -function POSITIONABLE:ClearCargo() -self.__.Cargo={} -end -function POSITIONABLE:IsCargoEmpty() -local IsEmpty=true -for _,Cargo in pairs(self.__.Cargo)do -IsEmpty=false -break -end -return IsEmpty -end -function POSITIONABLE:CargoItemCount() -local ItemCount=0 -for CargoName,Cargo in pairs(self.__.Cargo)do -ItemCount=ItemCount+Cargo:GetCount() -end -return ItemCount -end -function POSITIONABLE:GetCargoBayFreeWeight() -if not self.__.CargoBayWeightLimit then -self:SetCargoBayWeightLimit() -end -local CargoWeight=0 -for CargoName,Cargo in pairs(self.__.Cargo)do -CargoWeight=CargoWeight+Cargo:GetWeight() -end -return self.__.CargoBayWeightLimit-CargoWeight -end -function POSITIONABLE:SetCargoBayWeightLimit(WeightLimit) -if WeightLimit then -self.__.CargoBayWeightLimit=WeightLimit -elseif self.__.CargoBayWeightLimit~=nil then -else -if self:IsAir()then -local Desc=self:GetDesc() -self:F({Desc=Desc}) -local Weights={ -["C-17A"]=35000, -["C-130"]=22000 -} -self.__.CargoBayWeightLimit=Weights[Desc.typeName]or(Desc.massMax-(Desc.massEmpty+Desc.fuelMassMax)) -elseif self:IsShip()then -local Desc=self:GetDesc() -self:F({Desc=Desc}) -local Weights={ -["Type_071"]=245000, -["LHA_Tarawa"]=500000, -["Ropucha-class"]=150000, -["Dry-cargo ship-1"]=70000, -["Dry-cargo ship-2"]=70000, -["Higgins_boat"]=3700, -["USS_Samuel_Chase"]=25000, -["LST_Mk2"]=2100000, -} -self.__.CargoBayWeightLimit=(Weights[Desc.typeName]or 50000) -else -local Desc=self:GetDesc() -local Weights={ -["AAV7"]=25, -["Bedford_MWD"]=8, -["Blitz_36-6700A"]=10, -["BMD-1"]=9, -["BMP-1"]=8, -["BMP-2"]=7, -["BMP-3"]=8, -["Boman"]=25, -["BTR-80"]=9, -["BTR-82A"]=9, -["BTR_D"]=12, -["Cobra"]=8, -["Land_Rover_101_FC"]=11, -["Land_Rover_109_S3"]=7, -["LAV-25"]=6, -["M-2 Bradley"]=6, -["M1043 HMMWV Armament"]=4, -["M1045 HMMWV TOW"]=4, -["M1126 Stryker ICV"]=9, -["M1134 Stryker ATGM"]=9, -["M2A1_halftrack"]=9, -["M-113"]=9, -["Marder"]=6, -["MCV-80"]=9, -["MLRS FDDM"]=4, -["MTLB"]=25, -["GAZ-66"]=8, -["GAZ-3307"]=12, -["GAZ-3308"]=14, -["Grad_FDDM"]=6, -["KAMAZ Truck"]=12, -["KrAZ6322"]=12, -["M 818"]=12, -["Tigr_233036"]=6, -["TPZ"]=10, -["UAZ-469"]=4, -["Ural-375"]=12, -["Ural-4320-31"]=14, -["Ural-4320 APA-5D"]=10, -["Ural-4320T"]=14, -["ZBD04A"]=7, -} -local CargoBayWeightLimit=(Weights[Desc.typeName]or 0)*95 -self.__.CargoBayWeightLimit=CargoBayWeightLimit -end -end -self:F({CargoBayWeightLimit=self.__.CargoBayWeightLimit}) -end -end -function POSITIONABLE:Flare(FlareColor) -self:F2() -trigger.action.signalFlare(self:GetVec3(),FlareColor,0) -end -function POSITIONABLE:FlareWhite() -self:F2() -trigger.action.signalFlare(self:GetVec3(),trigger.flareColor.White,0) -end -function POSITIONABLE:FlareYellow() -self:F2() -trigger.action.signalFlare(self:GetVec3(),trigger.flareColor.Yellow,0) -end -function POSITIONABLE:FlareGreen() -self:F2() -trigger.action.signalFlare(self:GetVec3(),trigger.flareColor.Green,0) -end -function POSITIONABLE:FlareRed() -self:F2() -local Vec3=self:GetVec3() -if Vec3 then -trigger.action.signalFlare(Vec3,trigger.flareColor.Red,0) -end -end -function POSITIONABLE:Smoke(SmokeColor,Range,AddHeight) -self:F2() -if Range then -local Vec3=self:GetRandomVec3(Range) -Vec3.y=Vec3.y+AddHeight or 0 -trigger.action.smoke(Vec3,SmokeColor) -else -local Vec3=self:GetVec3() -Vec3.y=Vec3.y+AddHeight or 0 -trigger.action.smoke(self:GetVec3(),SmokeColor) -end -end -function POSITIONABLE:SmokeGreen() -self:F2() -trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Green) -end -function POSITIONABLE:SmokeRed() -self:F2() -trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Red) -end -function POSITIONABLE:SmokeWhite() -self:F2() -trigger.action.smoke(self:GetVec3(),trigger.smokeColor.White) -end -function POSITIONABLE:SmokeOrange() -self:F2() -trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Orange) -end -function POSITIONABLE:SmokeBlue() -self:F2() -trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Blue) -end -function POSITIONABLE:IsInZone(Zone) -self:F2({self.PositionableName,Zone}) -if self:IsAlive()then -local IsInZone=Zone:IsVec3InZone(self:GetVec3()) -return IsInZone -end -return false -end -function POSITIONABLE:IsNotInZone(Zone) -self:F2({self.PositionableName,Zone}) -if self:IsAlive()then -local IsNotInZone=not Zone:IsVec3InZone(self:GetVec3()) -return IsNotInZone -else -return false -end -end -CONTROLLABLE={ -ClassName="CONTROLLABLE", -ControllableName="", -WayPointFunctions={}, -} -function CONTROLLABLE:New(ControllableName) -local self=BASE:Inherit(self,POSITIONABLE:New(ControllableName)) -self.ControllableName=ControllableName -self.TaskScheduler=SCHEDULER:New(self) -return self -end -function CONTROLLABLE:_GetController() -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local ControllableController=DCSControllable:getController() -return ControllableController -end -return nil -end -function CONTROLLABLE:GetLife() -self:F2(self.ControllableName) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local UnitLife=0 -local Units=self:GetUnits() -if#Units==1 then -local Unit=Units[1] -UnitLife=Unit:GetLife() -else -local UnitLifeTotal=0 -for UnitID,Unit in pairs(Units)do -local Unit=Unit -UnitLifeTotal=UnitLifeTotal+Unit:GetLife() -end -UnitLife=UnitLifeTotal/#Units -end -return UnitLife -end -return nil -end -function CONTROLLABLE:GetLife0() -self:F2(self.ControllableName) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local UnitLife=0 -local Units=self:GetUnits() -if#Units==1 then -local Unit=Units[1] -UnitLife=Unit:GetLife0() -else -local UnitLifeTotal=0 -for UnitID,Unit in pairs(Units)do -local Unit=Unit -UnitLifeTotal=UnitLifeTotal+Unit:GetLife0() -end -UnitLife=UnitLifeTotal/#Units -end -return UnitLife -end -return nil -end -function CONTROLLABLE:GetFuelMin() -self:F(self.ControllableName) -return nil -end -function CONTROLLABLE:GetFuelAve() -self:F(self.ControllableName) -return nil -end -function CONTROLLABLE:GetFuel() -self:F(self.ControllableName) -return nil -end -function CONTROLLABLE:ClearTasks() -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -Controller:resetTask() -return self -end -return nil -end -function CONTROLLABLE:PopCurrentTask() -self:F2() -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -Controller:popTask() -return self -end -return nil -end -function CONTROLLABLE:PushTask(DCSTask,WaitTime) -self:F2() -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local DCSControllableName=self:GetName() -local function PushTask(Controller,DCSTask) -if self and self:IsAlive()then -local Controller=self:_GetController() -Controller:pushTask(DCSTask) -else -BASE:E({DCSControllableName.." is not alive anymore.",DCSTask=DCSTask}) -end -end -if not WaitTime or WaitTime==0 then -PushTask(self,DCSTask) -else -self.TaskScheduler:Schedule(self,PushTask,{DCSTask},WaitTime) -end -return self -end -return nil -end -function CONTROLLABLE:SetTask(DCSTask,WaitTime) -self:F({"SetTask",WaitTime,DCSTask=DCSTask}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local DCSControllableName=self:GetName() -self:T2("Controllable Name = "..DCSControllableName) -local function SetTask(Controller,DCSTask) -if self and self:IsAlive()then -local Controller=self:_GetController() -Controller:setTask(DCSTask) -self:T({ControllableName=self:GetName(),DCSTask=DCSTask}) -else -BASE:E({DCSControllableName.." is not alive anymore.",DCSTask=DCSTask}) -end -end -if not WaitTime or WaitTime==0 then -SetTask(self,DCSTask) -self:T({ControllableName=self:GetName(),DCSTask=DCSTask}) -else -self.TaskScheduler:Schedule(self,SetTask,{DCSTask},WaitTime) -end -return self -end -return nil -end -function CONTROLLABLE:HasTask() -local HasTaskResult=false -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -HasTaskResult=Controller:hasTask() -end -return HasTaskResult -end -function CONTROLLABLE:TaskCondition(time,userFlag,userFlagValue,condition,duration,lastWayPoint) -local DCSStopCondition={} -DCSStopCondition.time=time -DCSStopCondition.userFlag=userFlag -DCSStopCondition.userFlagValue=userFlagValue -DCSStopCondition.condition=condition -DCSStopCondition.duration=duration -DCSStopCondition.lastWayPoint=lastWayPoint -return DCSStopCondition -end -function CONTROLLABLE:TaskControlled(DCSTask,DCSStopCondition) -local DCSTaskControlled={ -id='ControlledTask', -params={ -task=DCSTask, -stopCondition=DCSStopCondition -} -} -return DCSTaskControlled -end -function CONTROLLABLE:TaskCombo(DCSTasks) -local DCSTaskCombo={ -id='ComboTask', -params={ -tasks=DCSTasks -} -} -return DCSTaskCombo -end -function CONTROLLABLE:TaskWrappedAction(DCSCommand,Index) -local DCSTaskWrappedAction={ -id="WrappedAction", -enabled=true, -number=Index or 1, -auto=false, -params={ -action=DCSCommand, -}, -} -return DCSTaskWrappedAction -end -function CONTROLLABLE:SetTaskWaypoint(Waypoint,Task) -Waypoint.task=self:TaskCombo({Task}) -self:F({Waypoint.task}) -return Waypoint.task -end -function CONTROLLABLE:SetCommand(DCSCommand) -self:F2(DCSCommand) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -Controller:setCommand(DCSCommand) -return self -end -return nil -end -function CONTROLLABLE:CommandSwitchWayPoint(FromWayPoint,ToWayPoint) -self:F2({FromWayPoint,ToWayPoint}) -local CommandSwitchWayPoint={ -id='SwitchWaypoint', -params={ -fromWaypointIndex=FromWayPoint, -goToWaypointIndex=ToWayPoint, -}, -} -self:T3({CommandSwitchWayPoint}) -return CommandSwitchWayPoint -end -function CONTROLLABLE:CommandStopRoute(StopRoute) -self:F2({StopRoute}) -local CommandStopRoute={ -id='StopRoute', -params={ -value=StopRoute, -}, -} -self:T3({CommandStopRoute}) -return CommandStopRoute -end -function CONTROLLABLE:StartUncontrolled(delay) -if delay and delay>0 then -SCHEDULER:New(nil,CONTROLLABLE.StartUncontrolled,{self},delay) -else -self:SetCommand({id='Start',params={}}) -end -return self -end -function CONTROLLABLE:CommandActivateBeacon(Type,System,Frequency,UnitID,Channel,ModeChannel,AA,Callsign,Bearing,Delay) -AA=AA or self:IsAir() -UnitID=UnitID or self:GetID() -local CommandActivateBeacon={ -id="ActivateBeacon", -params={ -["type"]=Type, -["system"]=System, -["frequency"]=Frequency, -["unitId"]=UnitID, -["channel"]=Channel, -["modeChannel"]=ModeChannel, -["AA"]=AA, -["callsign"]=Callsign, -["bearing"]=Bearing, -} -} -if Delay and Delay>0 then -SCHEDULER:New(nil,self.CommandActivateBeacon,{self,Type,System,Frequency,UnitID,Channel,ModeChannel,AA,Callsign,Bearing},Delay) -else -self:SetCommand(CommandActivateBeacon) -end -return self -end -function CONTROLLABLE:CommandActivateICLS(Channel,UnitID,Callsign,Delay) -local CommandActivateICLS={ -id="ActivateICLS", -params={ -["type"]=BEACON.Type.ICLS, -["channel"]=Channel, -["unitId"]=UnitID, -["callsign"]=Callsign, -} -} -if Delay and Delay>0 then -SCHEDULER:New(nil,self.CommandActivateICLS,{self},Delay) -else -self:SetCommand(CommandActivateICLS) -end -return self -end -function CONTROLLABLE:CommandDeactivateBeacon(Delay) -local CommandDeactivateBeacon={id='DeactivateBeacon',params={}} -if Delay and Delay>0 then -SCHEDULER:New(nil,self.CommandActivateBeacon,{self},Delay) -else -self:SetCommand(CommandDeactivateBeacon) -end -return self -end -function CONTROLLABLE:CommandDeactivateICLS(Delay) -local CommandDeactivateICLS={id='DeactivateICLS',params={}} -if Delay and Delay>0 then -SCHEDULER:New(nil,self.CommandDeactivateICLS,{self},Delay) -else -self:SetCommand(CommandDeactivateICLS) -end -return self -end -function CONTROLLABLE:CommandSetCallsign(CallName,CallNumber,Delay) -local CommandSetCallsign={id='SetCallsign',params={callname=CallName,number=CallNumber or 1}} -if Delay and Delay>0 then -SCHEDULER:New(nil,self.CommandSetCallsign,{self,CallName,CallNumber},Delay) -else -self:SetCommand(CommandSetCallsign) -end -return self -end -function CONTROLLABLE:CommandEPLRS(SwitchOnOff,Delay) -if SwitchOnOff==nil then -SwitchOnOff=true -end -local CommandEPLRS={ -id='EPLRS', -params={ -value=SwitchOnOff, -groupId=self:GetID() -} -} -if Delay and Delay>0 then -SCHEDULER:New(nil,self.CommandEPLRS,{self,SwitchOnOff},Delay) -else -self:T(string.format("EPLRS=%s for controllable %s (id=%s)",tostring(SwitchOnOff),tostring(self:GetName()),tostring(self:GetID()))) -self:SetCommand(CommandEPLRS) -end -return self -end -function CONTROLLABLE:CommandSetFrequency(Frequency,Modulation,Delay) -local CommandSetFrequency={ -id='SetFrequency', -params={ -frequency=Frequency*1000000, -modulation=Modulation or radio.modulation.AM, -} -} -if Delay and Delay>0 then -SCHEDULER:New(nil,self.CommandSetFrequency,{self,Frequency,Modulation},Delay) -else -self:SetCommand(CommandSetFrequency) -end -return self -end -function CONTROLLABLE:TaskEPLRS(SwitchOnOff,idx) -if SwitchOnOff==nil then -SwitchOnOff=true -end -local CommandEPLRS={ -id='EPLRS', -params={ -value=SwitchOnOff, -groupId=self:GetID() -} -} -return self:TaskWrappedAction(CommandEPLRS,idx or 1) -end -function CONTROLLABLE:TaskAttackGroup(AttackGroup,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit,GroupAttack) -local DCSTask={id='AttackGroup', -params={ -groupId=AttackGroup:GetID(), -weaponType=WeaponType or 1073741822, -expend=WeaponExpend or"Auto", -attackQtyLimit=AttackQty and true or false, -attackQty=AttackQty or 1, -directionEnabled=Direction and true or false, -direction=Direction and math.rad(Direction)or 0, -altitudeEnabled=Altitude and true or false, -altitude=Altitude, -groupAttack=GroupAttack and true or false, -}, -} -return DCSTask -end -function CONTROLLABLE:TaskAttackUnit(AttackUnit,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType) -local DCSTask={ -id='AttackUnit', -params={ -unitId=AttackUnit:GetID(), -groupAttack=GroupAttack and GroupAttack or false, -expend=WeaponExpend or"Auto", -directionEnabled=Direction and true or false, -direction=Direction and math.rad(Direction)or 0, -altitudeEnabled=Altitude and true or false, -altitude=Altitude, -attackQtyLimit=AttackQty and true or false, -attackQty=AttackQty, -weaponType=WeaponType or 1073741822, -} -} -return DCSTask -end -function CONTROLLABLE:TaskBombing(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType,Divebomb) -local DCSTask={ -id='Bombing', -params={ -point=Vec2, -x=Vec2.x, -y=Vec2.y, -groupAttack=GroupAttack and GroupAttack or false, -expend=WeaponExpend or"Auto", -attackQtyLimit=AttackQty and true or false, -attackQty=AttackQty or 1, -directionEnabled=Direction and true or false, -direction=Direction and math.rad(Direction)or 0, -altitudeEnabled=Altitude and true or false, -altitude=Altitude or 2000, -weaponType=WeaponType or 1073741822, -attackType=Divebomb and"Dive"or nil, -}, -} -return DCSTask -end -function CONTROLLABLE:TaskAttackMapObject(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType) -local DCSTask={ -id='AttackMapObject', -params={ -point=Vec2, -x=Vec2.x, -y=Vec2.y, -groupAttack=GroupAttack or false, -expend=WeaponExpend or"Auto", -attackQtyLimit=AttackQty and true or false, -attackQty=AttackQty, -directionEnabled=Direction and true or false, -direction=Direction and math.rad(Direction)or 0, -altitudeEnabled=Altitude and true or false, -altitude=Altitude, -weaponType=WeaponType or 1073741822, -}, -} -return DCSTask -end -function CONTROLLABLE:TaskCarpetBombing(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType,CarpetLength) -local DCSTask={ -id='CarpetBombing', -params={ -attackType="Carpet", -x=Vec2.x, -y=Vec2.y, -groupAttack=GroupAttack and GroupAttack or false, -carpetLength=CarpetLength or 500, -weaponType=WeaponType or ENUMS.WeaponFlag.AnyBomb, -expend=WeaponExpend or"All", -attackQtyLimit=AttackQty and true or false, -attackQty=AttackQty or 1, -directionEnabled=Direction and true or false, -direction=Direction and math.rad(Direction)or 0, -altitudeEnabled=Altitude and true or false, -altitude=Altitude, -} -} -return DCSTask -end -function CONTROLLABLE:TaskFollowBigFormation(FollowControllable,Vec3,LastWaypointIndex) -local DCSTask={ -id='FollowBigFormation', -params={ -groupId=FollowControllable:GetID(), -pos=Vec3, -lastWptIndexFlag=LastWaypointIndex and true or false, -lastWptIndex=LastWaypointIndex -} -} -return DCSTask -end -function CONTROLLABLE:TaskEmbarking(Coordinate,GroupSetForEmbarking,Duration,Distribution) -local g4e={} -if GroupSetForEmbarking then -for _,_group in pairs(GroupSetForEmbarking:GetSet())do -local group=_group -table.insert(g4e,group:GetID()) -end -else -self:E("ERROR: No groups for embarking specified!") -return nil -end -local groupID=self and self:GetID() -local DCSTask={ -id='Embarking', -params={ -selectedTransport=groupID, -x=Coordinate.x, -y=Coordinate.z, -groupsForEmbarking=g4e, -durationFlag=Duration and true or false, -duration=Duration, -distributionFlag=Distribution and true or false, -distribution=Distribution, -} -} -return DCSTask -end -function CONTROLLABLE:TaskEmbarkToTransport(Coordinate,Radius,UnitType) -local EmbarkToTransport={ -id="EmbarkToTransport", -params={ -x=Coordinate.x, -y=Coordinate.z, -zoneRadius=Radius or 200, -selectedType=UnitType, -} -} -return EmbarkToTransport -end -function CONTROLLABLE:TaskDisembarking(Coordinate,GroupSetToDisembark) -local g4e={} -if GroupSetToDisembark then -for _,_group in pairs(GroupSetToDisembark:GetSet())do -local group=_group -table.insert(g4e,group:GetID()) -end -else -self:E("ERROR: No groups for disembarking specified!") -return nil -end -local Disembarking={ -id="Disembarking", -params={ -x=Coordinate.x, -y=Coordinate.z, -groupsForEmbarking=g4e, -} -} -return Disembarking -end -function CONTROLLABLE:TaskOrbitCircleAtVec2(Point,Altitude,Speed) -self:F2({self.ControllableName,Point,Altitude,Speed}) -local DCSTask={ -id='Orbit', -params={ -pattern=AI.Task.OrbitPattern.CIRCLE, -point=Point, -speed=Speed, -altitude=Altitude+land.getHeight(Point) -} -} -return DCSTask -end -function CONTROLLABLE:TaskOrbit(Coord,Altitude,Speed,CoordRaceTrack) -local Pattern=AI.Task.OrbitPattern.CIRCLE -local P1=Coord:GetVec2() -local P2=nil -if CoordRaceTrack then -Pattern=AI.Task.OrbitPattern.RACE_TRACK -P2=CoordRaceTrack:GetVec2() -end -local Task={ -id='Orbit', -params={ -pattern=Pattern, -point=P1, -point2=P2, -speed=Speed or UTILS.KnotsToMps(250), -altitude=Altitude or Coord.y, -} -} -return Task -end -function CONTROLLABLE:TaskOrbitCircle(Altitude,Speed,Coordinate) -self:F2({self.ControllableName,Altitude,Speed}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local OrbitVec2=Coordinate and Coordinate:GetVec2()or self:GetVec2() -return self:TaskOrbitCircleAtVec2(OrbitVec2,Altitude,Speed) -end -return nil -end -function CONTROLLABLE:TaskHoldPosition() -self:F2({self.ControllableName}) -return self:TaskOrbitCircle(30,10) -end -function CONTROLLABLE:TaskBombingRunway(Airbase,WeaponType,WeaponExpend,AttackQty,Direction,GroupAttack) -local DCSTask={ -id='BombingRunway', -params={ -runwayId=Airbase:GetID(), -weaponType=WeaponType or ENUMS.WeaponFlag.AnyBomb, -expend=WeaponExpend or AI.Task.WeaponExpend.ALL, -attackQty=AttackQty or 1, -direction=Direction and math.rad(Direction)or 0, -groupAttack=GroupAttack and true or false, -}, -} -return DCSTask -end -function CONTROLLABLE:TaskRefueling() -local DCSTask={ -id='Refueling', -params={} -} -return DCSTask -end -function CONTROLLABLE:TaskLandAtVec2(Vec2,Duration) -local DCSTask={ -id='Land', -params={ -point=Vec2, -durationFlag=Duration and true or false, -duration=Duration, -}, -} -return DCSTask -end -function CONTROLLABLE:TaskLandAtZone(Zone,Duration,RandomPoint) -local Point=RandomPoint and Zone:GetRandomVec2()or Zone:GetVec2() -local DCSTask=CONTROLLABLE.TaskLandAtVec2(self,Point,Duration) -return DCSTask -end -function CONTROLLABLE:TaskFollow(FollowControllable,Vec3,LastWaypointIndex) -self:F2({self.ControllableName,FollowControllable,Vec3,LastWaypointIndex}) -local LastWaypointIndexFlag=false -local lastWptIndexFlagChangedManually=false -if LastWaypointIndex then -LastWaypointIndexFlag=true -lastWptIndexFlagChangedManually=true -end -local DCSTask={ -id='Follow', -params={ -groupId=FollowControllable:GetID(), -pos=Vec3, -lastWptIndexFlag=LastWaypointIndexFlag, -lastWptIndex=LastWaypointIndex, -lastWptIndexFlagChangedManually=lastWptIndexFlagChangedManually, -} -} -self:T3({DCSTask}) -return DCSTask -end -function CONTROLLABLE:TaskEscort(FollowControllable,Vec3,LastWaypointIndex,EngagementDistance,TargetTypes) -local DCSTask -DCSTask={ -id='Escort', -params={ -groupId=FollowControllable:GetID(), -pos=Vec3, -lastWptIndexFlag=LastWaypointIndex and true or false, -lastWptIndex=LastWaypointIndex, -engagementDistMax=EngagementDistance, -targetTypes=TargetTypes or{"Air"}, -}, -} -return DCSTask -end -function CONTROLLABLE:TaskFireAtPoint(Vec2,Radius,AmmoCount,WeaponType,Altitude,ASL) -local DCSTask={ -id='FireAtPoint', -params={ -point=Vec2, -x=Vec2.x, -y=Vec2.y, -zoneRadius=Radius, -radius=Radius, -expendQty=100, -expendQtyEnabled=false, -alt_type=ASL and 0 or 1 -} -} -if AmmoCount then -DCSTask.params.expendQty=AmmoCount -DCSTask.params.expendQtyEnabled=true -end -if Altitude then -DCSTask.params.altitude=Altitude -end -if WeaponType then -DCSTask.params.weaponType=WeaponType -end -return DCSTask -end -function CONTROLLABLE:TaskHold() -local DCSTask={id='Hold',params={}} -return DCSTask -end -function CONTROLLABLE:TaskFAC_AttackGroup(AttackGroup,WeaponType,Designation,Datalink,Frequency,Modulation,CallsignName,CallsignNumber) -local DCSTask={ -id='FAC_AttackGroup', -params={ -groupId=AttackGroup:GetID(), -weaponType=WeaponType or ENUMS.WeaponFlag.AutoDCS, -designation=Designation or"Auto", -datalink=Datalink and Datalink or true, -frequency=(Frequency or 133)*1000000, -modulation=Modulation or radio.modulation.AM, -callname=CallsignName, -number=CallsignNumber, -} -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskEngageTargets(Distance,TargetTypes,Priority) -local DCSTask={ -id='EngageTargets', -params={ -maxDistEnabled=Distance and true or false, -maxDist=Distance, -targetTypes=TargetTypes or{"Air"}, -priority=Priority or 0, -} -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskEngageTargetsInZone(Vec2,Radius,TargetTypes,Priority) -local DCSTask={ -id='EngageTargetsInZone', -params={ -point=Vec2, -zoneRadius=Radius, -targetTypes=TargetTypes or{"Air"}, -priority=Priority or 0 -} -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskEngageGroup(AttackGroup,Priority,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit) -local DCSTask={ -id='EngageControllable', -params={ -groupId=AttackGroup:GetID(), -weaponType=WeaponType, -expend=WeaponExpend or"Auto", -directionEnabled=Direction and true or false, -direction=Direction, -altitudeEnabled=Altitude and true or false, -altitude=Altitude, -attackQtyLimit=AttackQty and true or false, -attackQty=AttackQty, -priority=Priority or 1, -}, -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskEngageUnit(EngageUnit,Priority,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,Visible,ControllableAttack) -local DCSTask={ -id='EngageUnit', -params={ -unitId=EngageUnit:GetID(), -priority=Priority or 1, -groupAttack=GroupAttack and GroupAttack or false, -visible=Visible and Visible or false, -expend=WeaponExpend or"Auto", -directionEnabled=Direction and true or false, -direction=Direction and math.rad(Direction)or nil, -altitudeEnabled=Altitude and true or false, -altitude=Altitude, -attackQtyLimit=AttackQty and true or false, -attackQty=AttackQty, -controllableAttack=ControllableAttack, -}, -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskAWACS() -local DCSTask={ -id='AWACS', -params={}, -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskTanker() -local DCSTask={ -id='Tanker', -params={}, -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskEWR() -local DCSTask={ -id='EWR', -params={}, -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskFAC_EngageGroup(AttackGroup,Priority,WeaponType,Designation,Datalink) -local DCSTask={ -id='FAC_EngageControllable', -params={ -groupId=AttackGroup:GetID(), -weaponType=WeaponType or"Auto", -designation=Designation, -datalink=Datalink and Datalink or false, -priority=Priority or 0, -} -} -return DCSTask -end -function CONTROLLABLE:EnRouteTaskFAC(Radius,Priority) -local DCSTask={ -id='FAC', -params={ -radius=Radius, -priority=Priority -} -} -return DCSTask -end -function CONTROLLABLE:TaskFunction(FunctionString,...) -local DCSScript={} -DCSScript[#DCSScript+1]="local MissionControllable = GROUP:Find( ... ) " -if arg and arg.n>0 then -local ArgumentKey='_'..tostring(arg):match("table: (.*)") -self:SetState(self,ArgumentKey,arg) -DCSScript[#DCSScript+1]="local Arguments = MissionControllable:GetState( MissionControllable, '"..ArgumentKey.."' ) " -DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable, unpack( Arguments ) )" -else -DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable )" -end -local DCSTask=self:TaskWrappedAction(self:CommandDoScript(table.concat(DCSScript))) -return DCSTask -end -function CONTROLLABLE:TaskMission(TaskMission) -local DCSTask={ -id='Mission', -params={TaskMission,}, -} -return DCSTask -end -do -function CONTROLLABLE:PatrolRoute() -local PatrolGroup=self -if not self:IsInstanceOf("GROUP")then -PatrolGroup=self:GetGroup() -end -self:F({PatrolGroup=PatrolGroup:GetName()}) -if PatrolGroup:IsGround()or PatrolGroup:IsShip()then -local Waypoints=PatrolGroup:GetTemplateRoutePoints() -local FromCoord=PatrolGroup:GetCoordinate() -local From=FromCoord:WaypointGround(120) -table.insert(Waypoints,1,From) -local TaskRoute=PatrolGroup:TaskFunction("CONTROLLABLE.PatrolRoute") -self:F({Waypoints=Waypoints}) -local Waypoint=Waypoints[#Waypoints] -PatrolGroup:SetTaskWaypoint(Waypoint,TaskRoute) -PatrolGroup:Route(Waypoints) -end -end -function CONTROLLABLE:PatrolRouteRandom(Speed,Formation,ToWaypoint) -local PatrolGroup=self -if not self:IsInstanceOf("GROUP")then -PatrolGroup=self:GetGroup() -end -self:F({PatrolGroup=PatrolGroup:GetName()}) -if PatrolGroup:IsGround()or PatrolGroup:IsShip()then -local Waypoints=PatrolGroup:GetTemplateRoutePoints() -local FromCoord=PatrolGroup:GetCoordinate() -local FromWaypoint=1 -if ToWaypoint then -FromWaypoint=ToWaypoint -end -local ToWaypoint -repeat -ToWaypoint=math.random(1,#Waypoints) -until(ToWaypoint~=FromWaypoint) -self:F({FromWaypoint=FromWaypoint,ToWaypoint=ToWaypoint}) -local Waypoint=Waypoints[ToWaypoint] -local ToCoord=COORDINATE:NewFromVec2({x=Waypoint.x,y=Waypoint.y}) -local Route={} -Route[#Route+1]=FromCoord:WaypointGround(Speed,Formation) -Route[#Route+1]=ToCoord:WaypointGround(Speed,Formation) -local TaskRouteToZone=PatrolGroup:TaskFunction("CONTROLLABLE.PatrolRouteRandom",Speed,Formation,ToWaypoint) -PatrolGroup:SetTaskWaypoint(Route[#Route],TaskRouteToZone) -PatrolGroup:Route(Route,1) -end -end -function CONTROLLABLE:PatrolZones(ZoneList,Speed,Formation,DelayMin,DelayMax) -if not type(ZoneList)=="table"then -ZoneList={ZoneList} -end -local PatrolGroup=self -if not self:IsInstanceOf("GROUP")then -PatrolGroup=self:GetGroup() -end -DelayMin=DelayMin or 1 -if not DelayMax or DelayMaxLengthDirect*10)or(LengthRoad/LengthOnRoad*100<5)) -self:T(string.format("Length on road = %.3f km",LengthOnRoad/1000)) -self:T(string.format("Length directly = %.3f km",LengthDirect/1000)) -self:T(string.format("Length fraction = %.3f km",LengthOnRoad/LengthDirect)) -self:T(string.format("Length only road = %.3f km",LengthRoad/1000)) -self:T(string.format("Length off road = %.3f km",LengthOffRoad/1000)) -self:T(string.format("Percent on road = %.1f",LengthRoad/LengthOnRoad*100)) -end -local route={} -local canroad=false -if GotPath and LengthRoad and LengthDirect>2000 then -if LongRoad and Shortcut then -table.insert(route,FromCoordinate:WaypointGround(Speed,OffRoadFormation)) -table.insert(route,ToCoordinate:WaypointGround(Speed,OffRoadFormation)) -else -table.insert(route,FromCoordinate:WaypointGround(Speed,OffRoadFormation)) -table.insert(route,PathOnRoad[2]:WaypointGround(Speed,"On Road")) -table.insert(route,PathOnRoad[#PathOnRoad-1]:WaypointGround(Speed,"On Road")) -local dist=ToCoordinate:Get2DDistance(PathOnRoad[#PathOnRoad-1]) -if dist>10 then -table.insert(route,ToCoordinate:WaypointGround(Speed,OffRoadFormation)) -table.insert(route,ToCoordinate:GetRandomCoordinateInRadius(10,5):WaypointGround(5,OffRoadFormation)) -table.insert(route,ToCoordinate:GetRandomCoordinateInRadius(10,5):WaypointGround(5,OffRoadFormation)) -end -end -canroad=true -else -table.insert(route,FromCoordinate:WaypointGround(Speed,OffRoadFormation)) -table.insert(route,ToCoordinate:WaypointGround(Speed,OffRoadFormation)) -end -if WaypointFunction then -local N=#route -for n,waypoint in pairs(route)do -waypoint.task={} -waypoint.task.id="ComboTask" -waypoint.task.params={} -waypoint.task.params.tasks={self:TaskFunction("CONTROLLABLE.___PassingWaypoint",n,N,WaypointFunction,unpack(WaypointFunctionArguments or{}))} -end -end -return route,canroad -end -function CONTROLLABLE:TaskGroundOnRailRoads(ToCoordinate,Speed,WaypointFunction,WaypointFunctionArguments) -self:F2({ToCoordinate=ToCoordinate,Speed=Speed}) -Speed=Speed or 20 -local FromCoordinate=self:GetCoordinate() -local PathOnRail,LengthOnRail=FromCoordinate:GetPathOnRoad(ToCoordinate,false,true) -self:T(string.format("Length on railroad = %.3f km",LengthOnRail/1000)) -local route={} -if PathOnRail then -table.insert(route,PathOnRail[1]:WaypointGround(Speed,"On Railroad")) -table.insert(route,PathOnRail[2]:WaypointGround(Speed,"On Railroad")) -end -if WaypointFunction then -local N=#route -for n,waypoint in pairs(route)do -waypoint.task={} -waypoint.task.id="ComboTask" -waypoint.task.params={} -waypoint.task.params.tasks={self:TaskFunction("CONTROLLABLE.___PassingWaypoint",n,N,WaypointFunction,unpack(WaypointFunctionArguments or{}))} -end -end -return route -end -function CONTROLLABLE.___PassingWaypoint(controllable,n,N,waypointfunction,...) -waypointfunction(controllable,n,N,...) -end -function CONTROLLABLE:RouteAirTo(ToCoordinate,AltType,Type,Action,Speed,DelaySeconds) -local FromCoordinate=self:GetCoordinate() -local FromWP=FromCoordinate:WaypointAir() -local ToWP=ToCoordinate:WaypointAir(AltType,Type,Action,Speed) -self:Route({FromWP,ToWP},DelaySeconds) -return self -end -function CONTROLLABLE:TaskRouteToZone(Zone,Randomize,Speed,Formation) -self:F2(Zone) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local ControllablePoint=self:GetVec2() -local PointFrom={} -PointFrom.x=ControllablePoint.x -PointFrom.y=ControllablePoint.y -PointFrom.type="Turning Point" -PointFrom.action=Formation or"Cone" -PointFrom.speed=20/3.6 -local PointTo={} -local ZonePoint -if Randomize then -ZonePoint=Zone:GetRandomVec2() -else -ZonePoint=Zone:GetVec2() -end -PointTo.x=ZonePoint.x -PointTo.y=ZonePoint.y -PointTo.type="Turning Point" -if Formation then -PointTo.action=Formation -else -PointTo.action="Cone" -end -if Speed then -PointTo.speed=Speed -else -PointTo.speed=20/3.6 -end -local Points={PointFrom,PointTo} -self:T3(Points) -self:Route(Points) -return self -end -return nil -end -function CONTROLLABLE:TaskRouteToVec2(Vec2,Speed,Formation) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local ControllablePoint=self:GetVec2() -local PointFrom={} -PointFrom.x=ControllablePoint.x -PointFrom.y=ControllablePoint.y -PointFrom.type="Turning Point" -PointFrom.action=Formation or"Cone" -PointFrom.speed=20/3.6 -local PointTo={} -PointTo.x=Vec2.x -PointTo.y=Vec2.y -PointTo.type="Turning Point" -if Formation then -PointTo.action=Formation -else -PointTo.action="Cone" -end -if Speed then -PointTo.speed=Speed -else -PointTo.speed=20/3.6 -end -local Points={PointFrom,PointTo} -self:T3(Points) -self:Route(Points) -return self -end -return nil -end -end -function CONTROLLABLE:CommandDoScript(DoScript) -local DCSDoScript={ -id="Script", -params={ -command=DoScript, -}, -} -self:T3(DCSDoScript) -return DCSDoScript -end -function CONTROLLABLE:GetTaskMission() -self:F2(self.ControllableName) -return routines.utils.deepCopy(_DATABASE.Templates.Controllables[self.ControllableName].Template) -end -function CONTROLLABLE:GetTaskRoute() -self:F2(self.ControllableName) -return routines.utils.deepCopy(_DATABASE.Templates.Controllables[self.ControllableName].Template.route.points) -end -function CONTROLLABLE:CopyRoute(Begin,End,Randomize,Radius) -self:F2({Begin,End}) -local Points={} -local ControllableName=string.match(self:GetName(),".*#") -if ControllableName then -ControllableName=ControllableName:sub(1,-2) -else -ControllableName=self:GetName() -end -self:T3({ControllableName}) -local Template=_DATABASE.Templates.Controllables[ControllableName].Template -if Template then -if not Begin then -Begin=0 -end -if not End then -End=0 -end -for TPointID=Begin+1,#Template.route.points-End do -if Template.route.points[TPointID]then -Points[#Points+1]=routines.utils.deepCopy(Template.route.points[TPointID]) -if Randomize then -if not Radius then -Radius=500 -end -Points[#Points].x=Points[#Points].x+math.random(Radius*-1,Radius) -Points[#Points].y=Points[#Points].y+math.random(Radius*-1,Radius) -end -end -end -return Points -else -error("Template not found for Controllable : "..ControllableName) -end -return nil -end -function CONTROLLABLE:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -self:F2(self.ControllableName) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local DetectionVisual=(DetectVisual and DetectVisual==true)and Controller.Detection.VISUAL or nil -local DetectionOptical=(DetectOptical and DetectOptical==true)and Controller.Detection.OPTICAL or nil -local DetectionRadar=(DetectRadar and DetectRadar==true)and Controller.Detection.RADAR or nil -local DetectionIRST=(DetectIRST and DetectIRST==true)and Controller.Detection.IRST or nil -local DetectionRWR=(DetectRWR and DetectRWR==true)and Controller.Detection.RWR or nil -local DetectionDLINK=(DetectDLINK and DetectDLINK==true)and Controller.Detection.DLINK or nil -local Params={} -if DetectionVisual then -Params[#Params+1]=DetectionVisual -end -if DetectionOptical then -Params[#Params+1]=DetectionOptical -end -if DetectionRadar then -Params[#Params+1]=DetectionRadar -end -if DetectionIRST then -Params[#Params+1]=DetectionIRST -end -if DetectionRWR then -Params[#Params+1]=DetectionRWR -end -if DetectionDLINK then -Params[#Params+1]=DetectionDLINK -end -self:T2({DetectionVisual,DetectionOptical,DetectionRadar,DetectionIRST,DetectionRWR,DetectionDLINK}) -return self:_GetController():getDetectedTargets(Params[1],Params[2],Params[3],Params[4],Params[5],Params[6]) -end -return nil -end -function CONTROLLABLE:IsTargetDetected(DCSObject,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -self:F2(self.ControllableName) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local DetectionVisual=(DetectVisual and DetectVisual==true)and Controller.Detection.VISUAL or nil -local DetectionOptical=(DetectOptical and DetectOptical==true)and Controller.Detection.OPTICAL or nil -local DetectionRadar=(DetectRadar and DetectRadar==true)and Controller.Detection.RADAR or nil -local DetectionIRST=(DetectIRST and DetectIRST==true)and Controller.Detection.IRST or nil -local DetectionRWR=(DetectRWR and DetectRWR==true)and Controller.Detection.RWR or nil -local DetectionDLINK=(DetectDLINK and DetectDLINK==true)and Controller.Detection.DLINK or nil -local Controller=self:_GetController() -local TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity -=Controller:isTargetDetected(DCSObject,DetectionVisual,DetectionOptical,DetectionRadar,DetectionIRST,DetectionRWR,DetectionDLINK) -return TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity -end -return nil -end -function CONTROLLABLE:IsUnitDetected(Unit,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -self:F2(self.ControllableName) -if Unit and Unit:IsAlive()then -return self:IsTargetDetected(Unit:GetDCSObject(),DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -end -return nil -end -function CONTROLLABLE:IsGroupDetected(Group,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -self:F2(self.ControllableName) -if Group and Group:IsAlive()then -for _,_unit in pairs(Group:GetUnits())do -local unit=_unit -if unit and unit:IsAlive()then -local isdetected=self:IsUnitDetected(unit,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -if isdetected then -return true -end -end -end -return false -end -return nil -end -function CONTROLLABLE:GetDetectedUnitSet(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -local detectedtargets=self:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -local unitset=SET_UNIT:New() -for DetectionObjectID,Detection in pairs(detectedtargets or{})do -local DetectedObject=Detection.object -if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then -local unit=UNIT:Find(DetectedObject) -if unit and unit:IsAlive()then -if not unitset:FindUnit(unit:GetName())then -unitset:AddUnit(unit) -end -end -end -end -return unitset -end -function CONTROLLABLE:GetDetectedGroupSet(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -local detectedtargets=self:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -local groupset=SET_GROUP:New() -for DetectionObjectID,Detection in pairs(detectedtargets or{})do -local DetectedObject=Detection.object -if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then -local unit=UNIT:Find(DetectedObject) -if unit and unit:IsAlive()then -local group=unit:GetGroup() -if group and not groupset:FindGroup(group:GetName())then -groupset:AddGroup(group) -end -end -end -end -return groupset -end -function CONTROLLABLE:SetOption(OptionID,OptionValue) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -Controller:setOption(OptionID,OptionValue) -return self -end -return nil -end -function CONTROLLABLE:OptionROE(ROEvalue) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.ROE,ROEvalue) -elseif self:IsGround()then -Controller:setOption(AI.Option.Ground.id.ROE,ROEvalue) -elseif self:IsShip()then -Controller:setOption(AI.Option.Naval.id.ROE,ROEvalue) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROEHoldFirePossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()or self:IsGround()or self:IsShip()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROEHoldFire() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.WEAPON_HOLD) -elseif self:IsGround()then -Controller:setOption(AI.Option.Ground.id.ROE,AI.Option.Ground.val.ROE.WEAPON_HOLD) -elseif self:IsShip()then -Controller:setOption(AI.Option.Naval.id.ROE,AI.Option.Naval.val.ROE.WEAPON_HOLD) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROEReturnFirePossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()or self:IsGround()or self:IsShip()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROEReturnFire() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.RETURN_FIRE) -elseif self:IsGround()then -Controller:setOption(AI.Option.Ground.id.ROE,AI.Option.Ground.val.ROE.RETURN_FIRE) -elseif self:IsShip()then -Controller:setOption(AI.Option.Naval.id.ROE,AI.Option.Naval.val.ROE.RETURN_FIRE) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROEOpenFirePossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()or self:IsGround()or self:IsShip()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROEOpenFire() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.OPEN_FIRE) -elseif self:IsGround()then -Controller:setOption(AI.Option.Ground.id.ROE,AI.Option.Ground.val.ROE.OPEN_FIRE) -elseif self:IsShip()then -Controller:setOption(AI.Option.Naval.id.ROE,AI.Option.Naval.val.ROE.OPEN_FIRE) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROEOpenFireWeaponFreePossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROEOpenFireWeaponFree() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.OPEN_FIRE_WEAPON_FREE) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROEWeaponFreePossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROEWeaponFree() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.WEAPON_FREE) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROTNoReactionPossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROTNoReaction() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROT(ROTvalue) -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,ROTvalue) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROTPassiveDefensePossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROTPassiveDefense() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROTEvadeFirePossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROTEvadeFire() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE) -end -return self -end -return nil -end -function CONTROLLABLE:OptionROTVerticalPossible() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -if self:IsAir()then -return true -end -return false -end -return nil -end -function CONTROLLABLE:OptionROTVertical() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE) -end -return self -end -return nil -end -function CONTROLLABLE:OptionAlarmStateAuto() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsGround()then -Controller:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.AUTO) -elseif self:IsShip()then -Controller:setOption(9,0) -end -return self -end -return nil -end -function CONTROLLABLE:OptionAlarmStateGreen() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsGround()then -Controller:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) -elseif self:IsShip()then -Controller:setOption(9,1) -end -return self -end -return nil -end -function CONTROLLABLE:OptionAlarmStateRed() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsGround()then -Controller:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED) -elseif self:IsShip()then -Controller:setOption(9,2) -end -return self -end -return nil -end -function CONTROLLABLE:OptionRTBBingoFuel(RTB) -self:F2({self.ControllableName}) -if RTB==nil then -RTB=true -end -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.RTB_ON_BINGO,RTB) -end -return self -end -return nil -end -function CONTROLLABLE:OptionRTBAmmo(WeaponsFlag) -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.RTB_ON_OUT_OF_AMMO,WeaponsFlag) -end -return self -end -return nil -end -function CONTROLLABLE:OptionAllowJettisonWeaponsOnThreat() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.PROHIBIT_JETT,false) -end -return self -end -return nil -end -function CONTROLLABLE:OptionKeepWeaponsOnThreat() -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if self:IsAir()then -Controller:setOption(AI.Option.Air.id.PROHIBIT_JETT,true) -end -return self -end -return nil -end -function CONTROLLABLE:OptionProhibitAfterburner(Prohibit) -self:F2({self.ControllableName}) -if Prohibit==nil then -Prohibit=true -end -if self:IsAir()then -self:SetOption(AI.Option.Air.id.PROHIBIT_AB,Prohibit) -end -return self -end -function CONTROLLABLE:OptionECM_Never() -self:F2({self.ControllableName}) -if self:IsAir()then -self:SetOption(AI.Option.Air.id.ECM_USING,0) -end -return self -end -function CONTROLLABLE:OptionECM_OnlyLockByRadar() -self:F2({self.ControllableName}) -if self:IsAir()then -self:SetOption(AI.Option.Air.id.ECM_USING,1) -end -return self -end -function CONTROLLABLE:OptionECM_DetectedLockByRadar() -self:F2({self.ControllableName}) -if self:IsAir()then -self:SetOption(AI.Option.Air.id.ECM_USING,2) -end -return self -end -function CONTROLLABLE:OptionECM_AlwaysOn() -self:F2({self.ControllableName}) -if self:IsAir()then -self:SetOption(AI.Option.Air.id.ECM_USING,3) -end -return self -end -function CONTROLLABLE:WayPointInitialize(WayPoints) -self:F({WayPoints}) -if WayPoints then -self.WayPoints=WayPoints -else -self.WayPoints=self:GetTaskRoute() -end -return self -end -function CONTROLLABLE:GetWayPoints() -self:F() -if self.WayPoints then -return self.WayPoints -end -return nil -end -function CONTROLLABLE:WayPointFunction(WayPoint,WayPointIndex,WayPointFunction,...) -self:F2({WayPoint,WayPointIndex,WayPointFunction}) -table.insert(self.WayPoints[WayPoint].task.params.tasks,WayPointIndex) -self.WayPoints[WayPoint].task.params.tasks[WayPointIndex]=self:TaskFunction(WayPointFunction,arg) -return self -end -function CONTROLLABLE:WayPointExecute(WayPoint,WaitTime) -self:F({WayPoint,WaitTime}) -if not WayPoint then -WayPoint=1 -end -for TaskPointID=1,WayPoint-1 do -table.remove(self.WayPoints,1) -end -self:T3(self.WayPoints) -self:SetTask(self:TaskRoute(self.WayPoints),WaitTime) -return self -end -function CONTROLLABLE:IsAirPlane() -self:F2() -local DCSObject=self:GetDCSObject() -if DCSObject then -local Category=DCSObject:getDesc().category -return Category==Unit.Category.AIRPLANE -end -return nil -end -function CONTROLLABLE:IsHelicopter() -self:F2() -local DCSObject=self:GetDCSObject() -if DCSObject then -local Category=DCSObject:getDesc().category -return Category==Unit.Category.HELICOPTER -end -return nil -end -function CONTROLLABLE:OptionRestrictBurner(RestrictBurner) -self:F2({self.ControllableName}) -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if Controller then -if RestrictBurner==true then -if self:IsAir()then -Controller:setOption(16,true) -end -else -if self:IsAir()then -Controller:setOption(16,false) -end -end -end -end -end -function CONTROLLABLE:OptionAAAttackRange(range) -self:F2({self.ControllableName}) -local range=range or 3 -if range<0 or range>4 then -range=3 -end -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if Controller then -if self:IsAir()then -self:SetOption(AI.Option.Air.val.MISSILE_ATTACK,range) -end -end -return self -end -return nil -end -function CONTROLLABLE:OptionEngageRange(EngageRange) -self:F2({self.ControllableName}) -EngageRange=EngageRange or 100 -if EngageRange<0 or EngageRange>100 then -EngageRange=100 -end -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if Controller then -if self:IsGround()then -self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,EngageRange) -end -end -return self -end -return nil -end -function CONTROLLABLE:RelocateGroundRandomInRadius(speed,radius,onroad,shortcut) -self:F2({self.ControllableName}) -local _coord=self:GetCoordinate() -local _radius=radius or 500 -local _speed=speed or 20 -local _tocoord=_coord:GetRandomCoordinateInRadius(_radius,100) -local _onroad=onroad or true -local _grptsk={} -local _candoroad=false -local _shortcut=shortcut or false -if onroad then -_grptsk,_candoroad=self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut) -self:Route(_grptsk,5) -else -self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road") -end -return self -end -function CONTROLLABLE:OptionDisperseOnAttack(Seconds) -self:F2({self.ControllableName}) -local seconds=Seconds or 0 -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=self:_GetController() -if Controller then -if self:IsGround()then -self:SetOption(AI.Option.GROUND.id.DISPERSE_ON_ATTACK,seconds) -end -end -return self -end -return nil -end -GROUP={ -ClassName="GROUP", -} -GROUP.Takeoff={ -Air=1, -Runway=2, -Hot=3, -Cold=4, -} -GROUPTEMPLATE={} -GROUPTEMPLATE.Takeoff={ -[GROUP.Takeoff.Air]={"Turning Point","Turning Point"}, -[GROUP.Takeoff.Runway]={"TakeOff","From Runway"}, -[GROUP.Takeoff.Hot]={"TakeOffParkingHot","From Parking Area Hot"}, -[GROUP.Takeoff.Cold]={"TakeOffParking","From Parking Area"} -} -GROUP.Attribute={ -AIR_TRANSPORTPLANE="Air_TransportPlane", -AIR_AWACS="Air_AWACS", -AIR_FIGHTER="Air_Fighter", -AIR_BOMBER="Air_Bomber", -AIR_TANKER="Air_Tanker", -AIR_TRANSPORTHELO="Air_TransportHelo", -AIR_ATTACKHELO="Air_AttackHelo", -AIR_UAV="Air_UAV", -AIR_OTHER="Air_OtherAir", -GROUND_APC="Ground_APC", -GROUND_TRUCK="Ground_Truck", -GROUND_INFANTRY="Ground_Infantry", -GROUND_ARTILLERY="Ground_Artillery", -GROUND_TANK="Ground_Tank", -GROUND_TRAIN="Ground_Train", -GROUND_EWR="Ground_EWR", -GROUND_AAA="Ground_AAA", -GROUND_SAM="Ground_SAM", -GROUND_OTHER="Ground_OtherGround", -NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier", -NAVAL_WARSHIP="Naval_WarShip", -NAVAL_ARMEDSHIP="Naval_ArmedShip", -NAVAL_UNARMEDSHIP="Naval_UnarmedShip", -NAVAL_OTHER="Naval_OtherNaval", -OTHER_UNKNOWN="Other_Unknown", -} -function GROUP:NewTemplate(GroupTemplate,CoalitionSide,CategoryID,CountryID) -local GroupName=GroupTemplate.name -_DATABASE:_RegisterGroupTemplate(GroupTemplate,CoalitionSide,CategoryID,CountryID,GroupName) -local self=BASE:Inherit(self,CONTROLLABLE:New(GroupName)) -self.GroupName=GroupName -if not _DATABASE.GROUPS[GroupName]then -_DATABASE.GROUPS[GroupName]=self -end -self:SetEventPriority(4) -return self -end -function GROUP:Register(GroupName) -local self=BASE:Inherit(self,CONTROLLABLE:New(GroupName)) -self.GroupName=GroupName -self:SetEventPriority(4) -return self -end -function GROUP:Find(DCSGroup) -local GroupName=DCSGroup:getName() -local GroupFound=_DATABASE:FindGroup(GroupName) -return GroupFound -end -function GROUP:FindByName(GroupName) -local GroupFound=_DATABASE:FindGroup(GroupName) -return GroupFound -end -function GROUP:GetDCSObject() -local DCSGroup=Group.getByName(self.GroupName) -if DCSGroup then -return DCSGroup -end -return nil -end -function GROUP:GetPositionVec3() -self:F2(self.PositionableName) -local DCSPositionable=self:GetDCSObject() -if DCSPositionable then -local unit=DCSPositionable:getUnits()[1] -if unit then -local PositionablePosition=unit:getPosition().p -self:T3(PositionablePosition) -return PositionablePosition -end -end -return nil -end -function GROUP:IsAlive() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -if DCSGroup:isExist()then -local DCSUnit=DCSGroup:getUnit(1) -if DCSUnit then -local GroupIsAlive=DCSUnit:isActive() -self:T3(GroupIsAlive) -return GroupIsAlive -end -end -end -return nil -end -function GROUP:IsActive() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local unit=DCSGroup:getUnit(1) -if unit then -local GroupIsActive=unit:isActive() -return GroupIsActive -end -end -return nil -end -function GROUP:Destroy(GenerateEvent,delay) -self:F2(self.GroupName) -if delay and delay>0 then -self:ScheduleOnce(delay,GROUP.Destroy,self,GenerateEvent) -else -local DCSGroup=self:GetDCSObject() -if DCSGroup then -for Index,UnitData in pairs(DCSGroup:getUnits())do -if GenerateEvent and GenerateEvent==true then -if self:IsAir()then -self:CreateEventCrash(timer.getTime(),UnitData) -else -self:CreateEventDead(timer.getTime(),UnitData) -end -elseif GenerateEvent==false then -else -self:CreateEventRemoveUnit(timer.getTime(),UnitData) -end -end -USERFLAG:New(self:GetName()):Set(100) -DCSGroup:destroy() -DCSGroup=nil -end -end -return nil -end -function GROUP:GetCategory() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local GroupCategory=DCSGroup:getCategory() -self:T3(GroupCategory) -return GroupCategory -end -return nil -end -function GROUP:GetCategoryName() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local CategoryNames={ -[Group.Category.AIRPLANE]="Airplane", -[Group.Category.HELICOPTER]="Helicopter", -[Group.Category.GROUND]="Ground Unit", -[Group.Category.SHIP]="Ship", -[Group.Category.TRAIN]="Train", -} -local GroupCategory=DCSGroup:getCategory() -self:T3(GroupCategory) -return CategoryNames[GroupCategory] -end -return nil -end -function GROUP:GetCoalition() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local GroupCoalition=DCSGroup:getCoalition() -self:T3(GroupCoalition) -return GroupCoalition -end -return nil -end -function GROUP:GetCountry() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local GroupCountry=DCSGroup:getUnit(1):getCountry() -self:T3(GroupCountry) -return GroupCountry -end -return nil -end -function GROUP:HasAttribute(attribute,all) -local _units=self:GetUnits() -local _allhave=true -local _onehas=false -for _,_unit in pairs(_units)do -local _unit=_unit -if _unit then -local _hastit=_unit:HasAttribute(attribute) -if _hastit==true then -_onehas=true -else -_allhave=false -end -end -end -if all==true then -return _allhave -else -return _onehas -end -end -function GROUP:GetSpeedMax() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local Units=self:GetUnits() -local speedmax=nil -for _,unit in pairs(Units)do -local unit=unit -local speed=unit:GetSpeedMax() -if speedmax==nil then -speedmax=speed -elseif speed0 then -self:ScheduleOnce(delay,GROUP.Activate,self) -else -trigger.action.activateGroup(self:GetDCSObject()) -end -return self -end -function GROUP:GetTypeName() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local GroupTypeName=DCSGroup:getUnit(1):getTypeName() -self:T3(GroupTypeName) -return(GroupTypeName) -end -return nil -end -function GROUP:GetPlayerName() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local PlayerName=DCSGroup:getUnit(1):getPlayerName() -self:T3(PlayerName) -return(PlayerName) -end -return nil -end -function GROUP:GetCallsign() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local GroupCallSign=DCSGroup:getUnit(1):getCallsign() -self:T3(GroupCallSign) -return GroupCallSign -end -BASE:E({"Cannot GetCallsign",Positionable=self,Alive=self:IsAlive()}) -return nil -end -function GROUP:GetVec2() -local Unit=self:GetUnit(1) -if Unit then -local vec2=Unit:GetVec2() -return vec2 -end -end -function GROUP:GetVec3() -local unit=self:GetUnit(1) -if unit then -local vec3=unit:GetVec3() -return vec3 -end -self:E("ERROR: Cannot get Vec3 of group "..tostring(self.GroupName)) -return nil -end -function GROUP:GetPointVec2() -self:F2(self.GroupName) -local FirstUnit=self:GetUnit(1) -if FirstUnit then -local FirstUnitPointVec2=FirstUnit:GetPointVec2() -self:T3(FirstUnitPointVec2) -return FirstUnitPointVec2 -end -BASE:E({"Cannot GetPointVec2",Group=self,Alive=self:IsAlive()}) -return nil -end -function GROUP:GetCoordinate() -local FirstUnit=self:GetUnit(1) -if FirstUnit then -local FirstUnitCoordinate=FirstUnit:GetCoordinate() -return FirstUnitCoordinate -end -BASE:E({"Cannot GetCoordinate",Group=self,Alive=self:IsAlive()}) -return nil -end -function GROUP:GetRandomVec3(Radius) -self:F2(self.GroupName) -local FirstUnit=self:GetUnit(1) -if FirstUnit then -local FirstUnitRandomPointVec3=FirstUnit:GetRandomVec3(Radius) -self:T3(FirstUnitRandomPointVec3) -return FirstUnitRandomPointVec3 -end -BASE:E({"Cannot GetRandomVec3",Group=self,Alive=self:IsAlive()}) -return nil -end -function GROUP:GetHeading() -self:F2(self.GroupName) -local GroupSize=self:GetSize() -local HeadingAccumulator=0 -local n=0 -if GroupSize then -for i=1,GroupSize do -local unit=self:GetUnit(i) -if unit and unit:IsAlive()then -HeadingAccumulator=HeadingAccumulator+unit:GetHeading() -n=n+1 -end -end -return math.floor(HeadingAccumulator/n) -end -BASE:E({"Cannot GetHeading",Group=self,Alive=self:IsAlive()}) -return nil -end -function GROUP:GetFuelMin() -self:F3(self.ControllableName) -if not self:GetDCSObject()then -BASE:E({"Cannot GetFuel",Group=self,Alive=self:IsAlive()}) -return 0 -end -local min=65535 -local unit=nil -local tmp=nil -for UnitID,UnitData in pairs(self:GetUnits())do -if UnitData and UnitData:IsAlive()then -tmp=UnitData:GetFuel() -if tmpGroupVelocityMax then -GroupVelocityMax=UnitVelocity -end -end -return GroupVelocityMax -end -return nil -end -function GROUP:GetMinHeight() -self:F2() -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local GroupHeightMin=999999999 -for Index,UnitData in pairs(DCSGroup:getUnits())do -local UnitData=UnitData -local UnitHeight=UnitData:getPoint() -if UnitHeightGroupHeightMax then -GroupHeightMax=UnitHeight -end -end -return GroupHeightMax -end -return nil -end -function GROUP:GetTemplate() -local GroupName=self:GetName() -return UTILS.DeepCopy(_DATABASE:GetGroupTemplate(GroupName)) -end -function GROUP:GetTemplateRoutePoints() -local GroupName=self:GetName() -return UTILS.DeepCopy(_DATABASE:GetGroupTemplate(GroupName).route.points) -end -function GROUP:SetTemplateControlled(Template,Controlled) -Template.uncontrolled=not Controlled -return Template -end -function GROUP:SetTemplateCountry(Template,CountryID) -Template.CountryID=CountryID -return Template -end -function GROUP:SetTemplateCoalition(Template,CoalitionID) -Template.CoalitionID=CoalitionID -return Template -end -function GROUP:InitHeading(Heading) -self.InitRespawnHeading=Heading -return self -end -function GROUP:InitHeight(Height) -self.InitRespawnHeight=Height -return self -end -function GROUP:InitZone(Zone) -self.InitRespawnZone=Zone -return self -end -function GROUP:InitRandomizePositionZone(PositionZone) -self.InitRespawnRandomizePositionZone=PositionZone -self.InitRespawnRandomizePositionInner=nil -self.InitRespawnRandomizePositionOuter=nil -return self -end -function GROUP:InitRandomizePositionRadius(OuterRadius,InnerRadius) -self.InitRespawnRandomizePositionZone=nil -self.InitRespawnRandomizePositionOuter=OuterRadius -self.InitRespawnRandomizePositionInner=InnerRadius -return self -end -function GROUP:InitCoordinate(coordinate) -self:F({coordinate=coordinate}) -self.InitCoord=coordinate -return self -end -function GROUP:InitRadioCommsOnOff(switch) -self:F({switch=switch}) -if switch==true or switch==nil then -self.InitRespawnRadio=true -else -self.InitRespawnRadio=false -end -return self -end -function GROUP:InitRadioFrequency(frequency) -self:F({frequency=frequency}) -self.InitRespawnFreq=frequency -return self -end -function GROUP:InitRadioModulation(modulation) -self:F({modulation=modulation}) -if modulation and modulation:lower()=="fm"then -self.InitRespawnModu=radio.modulation.FM -else -self.InitRespawnModu=radio.modulation.AM -end -return self -end -function GROUP:InitModex(modex) -self:F({modex=modex}) -if modex then -self.InitRespawnModex=tonumber(modex) -end -return self -end -function GROUP:Respawn(Template,Reset) -Template=Template or self:GetTemplate() -local function _Heading(course) -local h -if course<=180 then -h=math.rad(course) -else -h=-math.rad(360-course) -end -return h -end -if self:IsAlive()then -local Zone=self.InitRespawnZone -local Vec3=Zone and Zone:GetVec3()or self:GetVec3() -local From={x=Template.x,y=Template.y} -Template.x=Vec3.x -Template.y=Vec3.z -self:F(#Template.units) -if Reset==true then -for UnitID,UnitData in pairs(self:GetUnits())do -local GroupUnit=UnitData -self:F(GroupUnit:GetName()) -if GroupUnit:IsAlive()then -self:I("FF Alive") -local GroupUnitVec3=GroupUnit:GetVec3() -if Zone then -if self.InitRespawnRandomizePositionZone then -GroupUnitVec3=Zone:GetRandomVec3() -else -if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then -GroupUnitVec3=POINT_VEC3:NewFromVec2(From):GetRandomPointVec3InRadius(self.InitRespawnRandomizePositionsOuter,self.InitRespawnRandomizePositionsInner) -else -GroupUnitVec3=Zone:GetVec3() -end -end -end -if self.InitCoord then -GroupUnitVec3=self.InitCoord:GetVec3() -end -Template.units[UnitID].alt=self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y -if Zone then -Template.units[UnitID].x=(Template.units[UnitID].x-From.x)+GroupUnitVec3.x -Template.units[UnitID].y=(Template.units[UnitID].y-From.y)+GroupUnitVec3.z -else -Template.units[UnitID].x=GroupUnitVec3.x -Template.units[UnitID].y=GroupUnitVec3.z -end -Template.units[UnitID].heading=_Heading(self.InitRespawnHeading and self.InitRespawnHeading or GroupUnit:GetHeading()) -Template.units[UnitID].psi=-Template.units[UnitID].heading -self:F({UnitID,Template.units[UnitID],Template.units[UnitID]}) -end -end -elseif Reset==false then -for UnitID,TemplateUnitData in pairs(Template.units)do -self:F("Reset") -local GroupUnitVec3={x=TemplateUnitData.x,y=TemplateUnitData.alt,z=TemplateUnitData.y} -if Zone then -if self.InitRespawnRandomizePositionZone then -GroupUnitVec3=Zone:GetRandomVec3() -else -if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then -GroupUnitVec3=POINT_VEC3:NewFromVec2(From):GetRandomPointVec3InRadius(self.InitRespawnRandomizePositionsOuter,self.InitRespawnRandomizePositionsInner) -else -GroupUnitVec3=Zone:GetVec3() -end -end -end -if self.InitCoord then -GroupUnitVec3=self.InitCoord:GetVec3() -end -Template.units[UnitID].alt=self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y -Template.units[UnitID].x=(Template.units[UnitID].x-From.x)+GroupUnitVec3.x -Template.units[UnitID].y=(Template.units[UnitID].y-From.y)+GroupUnitVec3.z -Template.units[UnitID].heading=self.InitRespawnHeading and self.InitRespawnHeading or TemplateUnitData.heading -self:F({UnitID,Template.units[UnitID],Template.units[UnitID]}) -end -else -local units=self:GetUnits() -for UnitID,Unit in pairs(Template.units)do -for _,_unit in pairs(units)do -local unit=_unit -if unit:GetName()==Unit.name then -local coord=unit:GetCoordinate() -local heading=unit:GetHeading() -Unit.x=coord.x -Unit.y=coord.z -Unit.alt=coord.y -Unit.heading=math.rad(heading) -Unit.psi=-Unit.heading -end -end -end -end -end -if self.InitRespawnModex then -for UnitID=1,#Template.units do -Template.units[UnitID].onboard_num=string.format("%03d",self.InitRespawnModex+(UnitID-1)) -end -end -if self.InitRespawnRadio then -Template.communication=self.InitRespawnRadio -end -if self.InitRespawnFreq then -Template.frequency=self.InitRespawnFreq -end -if self.InitRespawnModu then -Template.modulation=self.InitRespawnModu -end -self:Destroy(false) -self:T({Template=Template}) -_DATABASE:Spawn(Template) -self:ResetEvents() -return self -end -function GROUP:RespawnAtCurrentAirbase(SpawnTemplate,Takeoff,Uncontrolled) -self:F2({SpawnTemplate,Takeoff,Uncontrolled}) -if self and self:IsAlive()then -local airbase=self:GetCoordinate():GetClosestAirbase() -if airbase then -self:F2("Closest airbase = "..airbase:GetName()) -else -self:E("ERROR: could not find closest airbase!") -return nil -end -Takeoff=Takeoff or SPAWN.Takeoff.Hot -local AirbaseCoord=airbase:GetCoordinate() -SpawnTemplate=SpawnTemplate or self:GetTemplate() -if SpawnTemplate then -local SpawnPoint=SpawnTemplate.route.points[1] -SpawnPoint.linkUnit=nil -SpawnPoint.helipadId=nil -SpawnPoint.airdromeId=nil -local AirbaseID=airbase:GetID() -local AirbaseCategory=airbase:GetAirbaseCategory() -if AirbaseCategory==Airbase.Category.SHIP or AirbaseCategory==Airbase.Category.HELIPAD then -SpawnPoint.linkUnit=AirbaseID -SpawnPoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.AIRDROME then -SpawnPoint.airdromeId=AirbaseID -end -SpawnPoint.type=GROUPTEMPLATE.Takeoff[Takeoff][1] -SpawnPoint.action=GROUPTEMPLATE.Takeoff[Takeoff][2] -local units=self:GetUnits() -local x -local y -for UnitID=1,#units do -local unit=units[UnitID] -local Parkingspot,TermialID,Distance=unit:GetCoordinate():GetClosestParkingSpot(airbase) -self:T2(string.format("Closest parking spot distance = %s, terminal ID=%s",tostring(Distance),tostring(TermialID))) -local uc=unit:GetCoordinate() -SpawnTemplate.units[UnitID].x=uc.x -SpawnTemplate.units[UnitID].y=uc.z -SpawnTemplate.units[UnitID].alt=uc.y -SpawnTemplate.units[UnitID].parking=TermialID -SpawnTemplate.units[UnitID].parking_id=nil -end -SpawnPoint.x=SpawnTemplate.units[1].x -SpawnPoint.y=SpawnTemplate.units[1].y -SpawnPoint.alt=SpawnTemplate.units[1].alt -SpawnTemplate.x=SpawnTemplate.units[1].x -SpawnTemplate.y=SpawnTemplate.units[1].y -SpawnTemplate.uncontrolled=Uncontrolled -if self.InitRespawnRadio then -SpawnTemplate.communication=self.InitRespawnRadio -end -if self.InitRespawnFreq then -SpawnTemplate.frequency=self.InitRespawnFreq -end -if self.InitRespawnModu then -SpawnTemplate.modulation=self.InitRespawnModu -end -self:Destroy(false) -_DATABASE:Spawn(SpawnTemplate) -self:ResetEvents() -return self -end -else -self:E("WARNING: GROUP is not alive!") -end -return nil -end -function GROUP:GetTaskMission() -self:F2(self.GroupName) -return routines.utils.deepCopy(_DATABASE.Templates.Groups[self.GroupName].Template) -end -function GROUP:GetTaskRoute() -self:F2(self.GroupName) -return routines.utils.deepCopy(_DATABASE.Templates.Groups[self.GroupName].Template.route.points) -end -function GROUP:CopyRoute(Begin,End,Randomize,Radius) -self:F2({Begin,End}) -local Points={} -local GroupName=string.match(self:GetName(),".*#") -if GroupName then -GroupName=GroupName:sub(1,-2) -else -GroupName=self:GetName() -end -self:T3({GroupName}) -local Template=_DATABASE.Templates.Groups[GroupName].Template -if Template then -if not Begin then -Begin=0 -end -if not End then -End=0 -end -for TPointID=Begin+1,#Template.route.points-End do -if Template.route.points[TPointID]then -Points[#Points+1]=routines.utils.deepCopy(Template.route.points[TPointID]) -if Randomize then -if not Radius then -Radius=500 -end -Points[#Points].x=Points[#Points].x+math.random(Radius*-1,Radius) -Points[#Points].y=Points[#Points].y+math.random(Radius*-1,Radius) -end -end -end -return Points -else -error("Template not found for Group : "..GroupName) -end -return nil -end -function GROUP:CalculateThreatLevelA2G() -local MaxThreatLevelA2G=0 -for UnitName,UnitData in pairs(self:GetUnits())do -local ThreatUnit=UnitData -local ThreatLevelA2G=ThreatUnit:GetThreatLevel() -if ThreatLevelA2G>MaxThreatLevelA2G then -MaxThreatLevelA2G=ThreatLevelA2G -end -end -self:T3(MaxThreatLevelA2G) -return MaxThreatLevelA2G -end -function GROUP:GetThreatLevel() -local threatlevelMax=0 -for UnitName,UnitData in pairs(self:GetUnits())do -local ThreatUnit=UnitData -local threatlevel=ThreatUnit:GetThreatLevel() -if threatlevel>threatlevelMax then -threatlevelMax=threatlevel -end -end -return threatlevelMax -end -function GROUP:InAir() -self:F2(self.GroupName) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -local DCSUnit=DCSGroup:getUnit(1) -if DCSUnit then -local GroupInAir=DCSGroup:getUnit(1):inAir() -self:T3(GroupInAir) -return GroupInAir -end -end -return nil -end -function GROUP:IsAirborne(AllUnits) -self:F2(self.GroupName) -local units=self:GetUnits() -if units then -if AllUnits then -for _,_unit in pairs(units)do -local unit=_unit -if unit then -local inair=unit:InAir() -if not inair then -return false -end -end -end -return true -else -for _,_unit in pairs(units)do -local unit=_unit -if unit then -local inair=unit:InAir() -if inair then -return true -end -end -return false -end -end -end -return nil -end -function GROUP:GetDCSDesc(n) -n=n or 1 -local unit=self:GetUnit(n) -if unit and unit:IsAlive()~=nil then -local desc=unit:GetDesc() -return desc -end -return nil -end -function GROUP:GetAttribute() -local attribute=GROUP.Attribute.OTHER_UNKNOWN -if self then -local transportplane=self:HasAttribute("Transports")and self:HasAttribute("Planes") -local awacs=self:HasAttribute("AWACS") -local fighter=self:HasAttribute("Fighters")or self:HasAttribute("Interceptors")or self:HasAttribute("Multirole fighters")or(self:HasAttribute("Bombers")and not self:HasAttribute("Strategic bombers")) -local bomber=self:HasAttribute("Strategic bombers") -local tanker=self:HasAttribute("Tankers") -local uav=self:HasAttribute("UAVs") -local transporthelo=self:HasAttribute("Transport helicopters") -local attackhelicopter=self:HasAttribute("Attack helicopters") -local apc=self:HasAttribute("Infantry carriers") -local truck=self:HasAttribute("Trucks")and self:GetCategory()==Group.Category.GROUND -local infantry=self:HasAttribute("Infantry") -local artillery=self:HasAttribute("Artillery") -local tank=self:HasAttribute("Old Tanks")or self:HasAttribute("Modern Tanks") -local aaa=self:HasAttribute("AAA") -local ewr=self:HasAttribute("EWR") -local sam=self:HasAttribute("SAM elements")and(not self:HasAttribute("AAA")) -local train=self:GetCategory()==Group.Category.TRAIN -local aircraftcarrier=self:HasAttribute("Aircraft Carriers") -local warship=self:HasAttribute("Heavy armed ships") -local armedship=self:HasAttribute("Armed ships") -local unarmedship=self:HasAttribute("Unarmed ships") -if transportplane then -attribute=GROUP.Attribute.AIR_TRANSPORTPLANE -elseif awacs then -attribute=GROUP.Attribute.AIR_AWACS -elseif fighter then -attribute=GROUP.Attribute.AIR_FIGHTER -elseif bomber then -attribute=GROUP.Attribute.AIR_BOMBER -elseif tanker then -attribute=GROUP.Attribute.AIR_TANKER -elseif transporthelo then -attribute=GROUP.Attribute.AIR_TRANSPORTHELO -elseif attackhelicopter then -attribute=GROUP.Attribute.AIR_ATTACKHELO -elseif uav then -attribute=GROUP.Attribute.AIR_UAV -elseif apc then -attribute=GROUP.Attribute.GROUND_APC -elseif infantry then -attribute=GROUP.Attribute.GROUND_INFANTRY -elseif artillery then -attribute=GROUP.Attribute.GROUND_ARTILLERY -elseif tank then -attribute=GROUP.Attribute.GROUND_TANK -elseif aaa then -attribute=GROUP.Attribute.GROUND_AAA -elseif ewr then -attribute=GROUP.Attribute.GROUND_EWR -elseif sam then -attribute=GROUP.Attribute.GROUND_SAM -elseif truck then -attribute=GROUP.Attribute.GROUND_TRUCK -elseif train then -attribute=GROUP.Attribute.GROUND_TRAIN -elseif aircraftcarrier then -attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER -elseif warship then -attribute=GROUP.Attribute.NAVAL_WARSHIP -elseif armedship then -attribute=GROUP.Attribute.NAVAL_ARMEDSHIP -elseif unarmedship then -attribute=GROUP.Attribute.NAVAL_UNARMEDSHIP -else -if self:IsGround()then -attribute=GROUP.Attribute.GROUND_OTHER -elseif self:IsShip()then -attribute=GROUP.Attribute.NAVAL_OTHER -elseif self:IsAir()then -attribute=GROUP.Attribute.AIR_OTHER -else -attribute=GROUP.Attribute.OTHER_UNKNOWN -end -end -end -return attribute -end -do -function GROUP:RouteRTB(RTBAirbase,Speed) -self:F({RTBAirbase:GetName(),Speed}) -local DCSGroup=self:GetDCSObject() -if DCSGroup then -if RTBAirbase then -local Speed=Speed or self:GetSpeedMax()*0.8 -local coord=self:GetCoordinate() -local PointFrom=coord:WaypointAirTurningPoint(nil,Speed) -local PointLanding=RTBAirbase:GetCoordinate():WaypointAirLanding(Speed,RTBAirbase) -local Points={PointFrom,PointLanding} -self:T3(Points) -local Template=self:GetTemplate() -Template.route.points=Points -self:Respawn(Template,true) -self:Route(Points) -else -self:ClearTasks() -end -end -return self -end -end -function GROUP:OnReSpawn(ReSpawnFunction) -self.ReSpawnFunction=ReSpawnFunction -end -do -function GROUP:HandleEvent(Event,EventFunction,...) -self:EventDispatcher():OnEventForGroup(self:GetName(),EventFunction,self,Event,...) -return self -end -function GROUP:UnHandleEvent(Event) -self:EventDispatcher():RemoveEvent(self,Event) -return self -end -function GROUP:ResetEvents() -self:EventDispatcher():Reset(self) -for UnitID,UnitData in pairs(self:GetUnits())do -UnitData:ResetEvents() -end -return self -end -end -do -function GROUP:GetPlayerNames() -local HasPlayers=false -local PlayerNames={} -local Units=self:GetUnits() -for UnitID,UnitData in pairs(Units)do -local Unit=UnitData -local PlayerName=Unit:GetPlayerName() -if PlayerName and PlayerName~=""then -PlayerNames=PlayerNames or{} -table.insert(PlayerNames,PlayerName) -HasPlayers=true -end -end -if HasPlayers==true then -self:F2(PlayerNames) -return PlayerNames -end -return nil -end -function GROUP:GetPlayerCount() -local PlayerCount=0 -local Units=self:GetUnits() -for UnitID,UnitData in pairs(Units or{})do -local Unit=UnitData -local PlayerName=Unit:GetPlayerName() -if PlayerName and PlayerName~=""then -PlayerCount=PlayerCount+1 -end -end -return PlayerCount -end -end -function GROUP:EnableEmission(switch) -self:F2(self.GroupName) -local switch=switch or false -local DCSUnit=self:GetDCSObject() -if DCSUnit then -DCSUnit:enableEmission(switch) -end -end -UNIT={ -ClassName="UNIT", -UnitName=nil, -} -function UNIT:Register(UnitName) -local self=BASE:Inherit(self,CONTROLLABLE:New(UnitName)) -self.UnitName=UnitName -self:SetEventPriority(3) -return self -end -function UNIT:Find(DCSUnit) -if DCSUnit then -local UnitName=DCSUnit:getName() -local UnitFound=_DATABASE:FindUnit(UnitName) -return UnitFound -end -return nil -end -function UNIT:FindByName(UnitName) -local UnitFound=_DATABASE:FindUnit(UnitName) -return UnitFound -end -function UNIT:Name() -return self.UnitName -end -function UNIT:GetDCSObject() -local DCSUnit=Unit.getByName(self.UnitName) -if DCSUnit then -return DCSUnit -end -return nil -end -function UNIT:ReSpawnAt(Coordinate,Heading) -self:T(self:Name()) -local SpawnGroupTemplate=UTILS.DeepCopy(_DATABASE:GetGroupTemplateFromUnitName(self:Name())) -self:T(SpawnGroupTemplate) -local SpawnGroup=self:GetGroup() -self:T({SpawnGroup=SpawnGroup}) -if SpawnGroup then -local Vec3=SpawnGroup:GetVec3() -SpawnGroupTemplate.x=Coordinate.x -SpawnGroupTemplate.y=Coordinate.z -self:F(#SpawnGroupTemplate.units) -for UnitID,UnitData in pairs(SpawnGroup:GetUnits())do -local GroupUnit=UnitData -self:F(GroupUnit:GetName()) -if GroupUnit:IsAlive()then -local GroupUnitVec3=GroupUnit:GetVec3() -local GroupUnitHeading=GroupUnit:GetHeading() -SpawnGroupTemplate.units[UnitID].alt=GroupUnitVec3.y -SpawnGroupTemplate.units[UnitID].x=GroupUnitVec3.x -SpawnGroupTemplate.units[UnitID].y=GroupUnitVec3.z -SpawnGroupTemplate.units[UnitID].heading=GroupUnitHeading -self:F({UnitID,SpawnGroupTemplate.units[UnitID],SpawnGroupTemplate.units[UnitID]}) -end -end -end -for UnitTemplateID,UnitTemplateData in pairs(SpawnGroupTemplate.units)do -self:T({UnitTemplateData.name,self:Name()}) -SpawnGroupTemplate.units[UnitTemplateID].unitId=nil -if UnitTemplateData.name==self:Name()then -self:T("Adjusting") -SpawnGroupTemplate.units[UnitTemplateID].alt=Coordinate.y -SpawnGroupTemplate.units[UnitTemplateID].x=Coordinate.x -SpawnGroupTemplate.units[UnitTemplateID].y=Coordinate.z -SpawnGroupTemplate.units[UnitTemplateID].heading=Heading -self:F({UnitTemplateID,SpawnGroupTemplate.units[UnitTemplateID],SpawnGroupTemplate.units[UnitTemplateID]}) -else -self:F(SpawnGroupTemplate.units[UnitTemplateID].name) -local GroupUnit=UNIT:FindByName(SpawnGroupTemplate.units[UnitTemplateID].name) -if GroupUnit and GroupUnit:IsAlive()then -local GroupUnitVec3=GroupUnit:GetVec3() -local GroupUnitHeading=GroupUnit:GetHeading() -UnitTemplateData.alt=GroupUnitVec3.y -UnitTemplateData.x=GroupUnitVec3.x -UnitTemplateData.y=GroupUnitVec3.z -UnitTemplateData.heading=GroupUnitHeading -else -if SpawnGroupTemplate.units[UnitTemplateID].name~=self:Name()then -self:T("nilling") -SpawnGroupTemplate.units[UnitTemplateID].delete=true -end -end -end -end -local i=1 -while i<=#SpawnGroupTemplate.units do -local UnitTemplateData=SpawnGroupTemplate.units[i] -self:T(UnitTemplateData.name) -if UnitTemplateData.delete then -table.remove(SpawnGroupTemplate.units,i) -else -i=i+1 -end -end -SpawnGroupTemplate.groupId=nil -self:T(SpawnGroupTemplate) -_DATABASE:Spawn(SpawnGroupTemplate) -end -function UNIT:IsActive() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitIsActive=DCSUnit:isActive() -return UnitIsActive -end -return nil -end -function UNIT:IsAlive() -self:F3(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitIsAlive=DCSUnit:isExist()and DCSUnit:isActive() -return UnitIsAlive -end -return nil -end -function UNIT:GetCallsign() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitCallSign=DCSUnit:getCallsign() -if UnitCallSign==""then -UnitCallSign=DCSUnit:getName() -end -return UnitCallSign -end -self:F(self.ClassName.." "..self.UnitName.." not found!") -return nil -end -function UNIT:IsPlayer() -local group=self:GetGroup() -local units=group:GetTemplate().units -for _,unit in pairs(units)do -if unit.name==self:GetName()and(unit.skill=="Client"or unit.skill=="Player")then -return true -end -end -return false -end -function UNIT:GetPlayerName() -self:F(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local PlayerName=DCSUnit:getPlayerName() -return PlayerName -end -return nil -end -function UNIT:IsClient() -if _DATABASE.CLIENTS[self.UnitName]then -return true -end -return false -end -function UNIT:GetClient() -local client=_DATABASE.CLIENTS[self.UnitName] -if client then -return client -end -return nil -end -function UNIT:GetNumber() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitNumber=DCSUnit:getNumber() -return UnitNumber -end -return nil -end -function UNIT:GetSpeedMax() -self:F2(self.UnitName) -local Desc=self:GetDesc() -if Desc then -local SpeedMax=Desc.speedMax -return SpeedMax*3.6 -end -return nil -end -function UNIT:GetRange() -self:F2(self.UnitName) -local Desc=self:GetDesc() -if Desc then -local Range=Desc.range -if Range then -Range=Range*1000 -else -Range=10000000 -end -return Range -end -return nil -end -function UNIT:IsRefuelable() -self:F2(self.UnitName) -local refuelable=self:HasAttribute("Refuelable") -local system=nil -local Desc=self:GetDesc() -if Desc and Desc.tankerType then -system=Desc.tankerType -end -return refuelable,system -end -function UNIT:IsTanker() -self:F2(self.UnitName) -local tanker=self:HasAttribute("Tankers") -local system=nil -if tanker then -local Desc=self:GetDesc() -if Desc and Desc.tankerType then -system=Desc.tankerType -end -local typename=self:GetTypeName() -if typename=="IL-78M"then -system=1 -elseif typename=="KC130"then -system=1 -elseif typename=="KC135BDA"then -system=1 -elseif typename=="KC135MPRS"then -system=1 -elseif typename=="S-3B Tanker"then -system=1 -end -end -return tanker,system -end -function UNIT:GetGroup() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitGroup=GROUP:FindByName(DCSUnit:getGroup():getName()) -return UnitGroup -end -return nil -end -function UNIT:GetPrefix() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitPrefix=string.match(self.UnitName,".*#"):sub(1,-2) -self:T3(UnitPrefix) -return UnitPrefix -end -return nil -end -function UNIT:GetAmmo() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitAmmo=DCSUnit:getAmmo() -return UnitAmmo -end -return nil -end -function UNIT:GetAmmunition() -local nammo=0 -local nshells=0 -local nrockets=0 -local nmissiles=0 -local nbombs=0 -local unit=self -local ammotable=unit:GetAmmo() -if ammotable then -local weapons=#ammotable -for w=1,weapons do -local Nammo=ammotable[w]["count"] -local Tammo=ammotable[w]["desc"]["typeName"] -local _weaponString=UTILS.Split(Tammo,"%.") -local _weaponName=_weaponString[#_weaponString] -local Category=ammotable[w].desc.category -local MissileCategory=nil -if Category==Weapon.Category.MISSILE then -MissileCategory=ammotable[w].desc.missileCategory -end -if Category==Weapon.Category.SHELL then -nshells=nshells+Nammo -elseif Category==Weapon.Category.ROCKET then -nrockets=nrockets+Nammo -elseif Category==Weapon.Category.BOMB then -nbombs=nbombs+Nammo -elseif Category==Weapon.Category.MISSILE then -if MissileCategory==Weapon.MissileCategory.AAM then -nmissiles=nmissiles+Nammo -elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then -nmissiles=nmissiles+Nammo -elseif MissileCategory==Weapon.MissileCategory.BM then -nmissiles=nmissiles+Nammo -elseif MissileCategory==Weapon.MissileCategory.OTHER then -nmissiles=nmissiles+Nammo -end -end -end -end -nammo=nshells+nrockets+nmissiles+nbombs -return nammo,nshells,nrockets,nbombs,nmissiles -end -function UNIT:GetSensors() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitSensors=DCSUnit:getSensors() -return UnitSensors -end -return nil -end -function UNIT:HasSensors(...) -self:F2(arg) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local HasSensors=DCSUnit:hasSensors(unpack(arg)) -return HasSensors -end -return nil -end -function UNIT:HasSEAD() -self:F2() -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitSEADAttributes=DCSUnit:getDesc().attributes -local HasSEAD=false -if UnitSEADAttributes["RADAR_BAND1_FOR_ARM"]and UnitSEADAttributes["RADAR_BAND1_FOR_ARM"]==true or -UnitSEADAttributes["RADAR_BAND2_FOR_ARM"]and UnitSEADAttributes["RADAR_BAND2_FOR_ARM"]==true then -HasSEAD=true -end -return HasSEAD -end -return nil -end -function UNIT:GetRadar() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitRadarOn,UnitRadarObject=DCSUnit:getRadar() -return UnitRadarOn,UnitRadarObject -end -return nil,nil -end -function UNIT:GetFuel() -self:F3(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitFuel=DCSUnit:getFuel() -return UnitFuel -end -return nil -end -function UNIT:SetEmission(Switch) -if Switch==nil then -Switch=true -end -local DCSUnit=self:GetDCSObject() -if DCSUnit then -DCSUnit:enableEmission(Switch) -end -return self -end -function UNIT:EnableEmission() -self:SetEmission(true) -return self -end -function UNIT:DisableEmission() -self:SetEmission(false) -return self -end -function UNIT:GetUnits() -self:F3({self.UnitName}) -local DCSUnit=self:GetDCSObject() -local Units={} -if DCSUnit then -Units[1]=UNIT:Find(DCSUnit) -self:T3(Units) -return Units -end -return nil -end -function UNIT:GetLife() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitLife=DCSUnit:getLife() -return UnitLife -end -return-1 -end -function UNIT:GetLife0() -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitLife0=DCSUnit:getLife0() -return UnitLife0 -end -return 0 -end -function UNIT:GetLifeRelative() -self:F2(self.UnitName) -if self and self:IsAlive()then -local life0=self:GetLife0() -local lifeN=self:GetLife() -return lifeN/life0 -end -return-1 -end -function UNIT:GetDamageRelative() -self:F2(self.UnitName) -if self and self:IsAlive()then -return 1-self:GetLifeRelative() -end -return 1 -end -function UNIT:GetUnitCategory() -self:F3(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -return DCSUnit:getDesc().category -end -return nil -end -function UNIT:GetCategoryName() -self:F3(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local CategoryNames={ -[Unit.Category.AIRPLANE]="Airplane", -[Unit.Category.HELICOPTER]="Helicopter", -[Unit.Category.GROUND_UNIT]="Ground Unit", -[Unit.Category.SHIP]="Ship", -[Unit.Category.STRUCTURE]="Structure", -} -local UnitCategory=DCSUnit:getDesc().category -self:T3(UnitCategory) -return CategoryNames[UnitCategory] -end -return nil -end -function UNIT:GetThreatLevel() -local ThreatLevel=0 -local ThreatText="" -local Descriptor=self:GetDesc() -if Descriptor then -local Attributes=Descriptor.attributes -if self:IsGround()then -local ThreatLevels={ -"Unarmed", -"Infantry", -"Old Tanks & APCs", -"Tanks & IFVs without ATGM", -"Tanks & IFV with ATGM", -"Modern Tanks", -"AAA", -"IR Guided SAMs", -"SR SAMs", -"MR SAMs", -"LR SAMs" -} -if Attributes["LR SAM"]then ThreatLevel=10 -elseif Attributes["MR SAM"]then ThreatLevel=9 -elseif Attributes["SR SAM"]and -not Attributes["IR Guided SAM"]then ThreatLevel=8 -elseif(Attributes["SR SAM"]or Attributes["MANPADS"])and -Attributes["IR Guided SAM"]then ThreatLevel=7 -elseif Attributes["AAA"]then ThreatLevel=6 -elseif Attributes["Modern Tanks"]then ThreatLevel=5 -elseif(Attributes["Tanks"]or Attributes["IFV"])and -Attributes["ATGM"]then ThreatLevel=4 -elseif(Attributes["Tanks"]or Attributes["IFV"])and -not Attributes["ATGM"]then ThreatLevel=3 -elseif Attributes["Old Tanks"]or Attributes["APC"]or Attributes["Artillery"]then ThreatLevel=2 -elseif Attributes["Infantry"]then ThreatLevel=1 -end -ThreatText=ThreatLevels[ThreatLevel+1] -end -if self:IsAir()then -local ThreatLevels={ -"Unarmed", -"Tanker", -"AWACS", -"Transport Helicopter", -"UAV", -"Bomber", -"Strategic Bomber", -"Attack Helicopter", -"Battleplane", -"Multirole Fighter", -"Fighter" -} -if Attributes["Fighters"]then ThreatLevel=10 -elseif Attributes["Multirole fighters"]then ThreatLevel=9 -elseif Attributes["Battleplanes"]then ThreatLevel=8 -elseif Attributes["Attack helicopters"]then ThreatLevel=7 -elseif Attributes["Strategic bombers"]then ThreatLevel=6 -elseif Attributes["Bombers"]then ThreatLevel=5 -elseif Attributes["UAVs"]then ThreatLevel=4 -elseif Attributes["Transport helicopters"]then ThreatLevel=3 -elseif Attributes["AWACS"]then ThreatLevel=2 -elseif Attributes["Tankers"]then ThreatLevel=1 -end -ThreatText=ThreatLevels[ThreatLevel+1] -end -if self:IsShip()then -local ThreatLevels={ -"Unarmed ship", -"Light armed ships", -"Corvettes", -"", -"Frigates", -"", -"Cruiser", -"", -"Destroyer", -"", -"Aircraft Carrier" -} -if Attributes["Aircraft Carriers"]then ThreatLevel=10 -elseif Attributes["Destroyers"]then ThreatLevel=8 -elseif Attributes["Cruisers"]then ThreatLevel=6 -elseif Attributes["Frigates"]then ThreatLevel=4 -elseif Attributes["Corvettes"]then ThreatLevel=2 -elseif Attributes["Light armed ships"]then ThreatLevel=1 -end -ThreatText=ThreatLevels[ThreatLevel+1] -end -end -return ThreatLevel,ThreatText -end -function UNIT:Explode(power,delay) -power=power or 100 -local DCSUnit=self:GetDCSObject() -if DCSUnit then -if delay and delay>0 then -SCHEDULER:New(nil,self.Explode,{self,power},delay) -else -self:GetCoordinate():Explosion(power) -end -return self -end -return nil -end -function UNIT:OtherUnitInRadius(AwaitUnit,Radius) -self:F2({self.UnitName,AwaitUnit.UnitName,Radius}) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitVec3=self:GetVec3() -local AwaitUnitVec3=AwaitUnit:GetVec3() -if(((UnitVec3.x-AwaitUnitVec3.x)^2+(UnitVec3.z-AwaitUnitVec3.z)^2)^0.5<=Radius)then -self:T3("true") -return true -else -self:T3("false") -return false -end -end -return nil -end -function UNIT:IsFriendly(FriendlyCoalition) -self:F2() -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitCoalition=DCSUnit:getCoalition() -self:T3({UnitCoalition,FriendlyCoalition}) -local IsFriendlyResult=(UnitCoalition==FriendlyCoalition) -self:F(IsFriendlyResult) -return IsFriendlyResult -end -return nil -end -function UNIT:IsShip() -self:F2() -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitDescriptor=DCSUnit:getDesc() -self:T3({UnitDescriptor.category,Unit.Category.SHIP}) -local IsShipResult=(UnitDescriptor.category==Unit.Category.SHIP) -self:T3(IsShipResult) -return IsShipResult -end -return nil -end -function UNIT:InAir(NoHeloCheck) -self:F2(self.UnitName) -local DCSUnit=self:GetDCSObject() -if DCSUnit then -local UnitInAir=DCSUnit:inAir() -local UnitCategory=DCSUnit:getDesc().category -if UnitInAir==true and UnitCategory==Unit.Category.HELICOPTER and(not NoHeloCheck)then -local VelocityVec3=DCSUnit:getVelocity() -local Velocity=UTILS.VecNorm(VelocityVec3) -local Coordinate=DCSUnit:getPoint() -local LandHeight=land.getHeight({x=Coordinate.x,y=Coordinate.z}) -local Height=Coordinate.y-LandHeight -if Velocity<1 and Height<=60 then -UnitInAir=false -end -end -self:T3(UnitInAir) -return UnitInAir -end -return nil -end -do -function UNIT:HandleEvent(EventID,EventFunction) -self:EventDispatcher():OnEventForUnit(self:GetName(),EventFunction,self,EventID) -return self -end -function UNIT:UnHandleEvent(EventID) -self:EventDispatcher():RemoveEvent(self,EventID) -return self -end -function UNIT:ResetEvents() -self:EventDispatcher():Reset(self) -return self -end -end -do -function UNIT:IsDetected(TargetUnit) -local TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity=self:IsTargetDetected(TargetUnit:GetDCSObject()) -return TargetIsDetected -end -function UNIT:IsLOS(TargetUnit) -local IsLOS=self:GetPointVec3():IsLOS(TargetUnit:GetPointVec3()) -return IsLOS -end -function UNIT:KnowUnit(TargetUnit,TypeKnown,DistanceKnown) -if TypeKnown~=false then -TypeKnown=true -end -if DistanceKnown~=false then -DistanceKnown=true -end -local DCSControllable=self:GetDCSObject() -if DCSControllable then -local Controller=DCSControllable:getController() -if Controller then -local object=TargetUnit:GetDCSObject() -if object then -self:I(string.format("Unit %s now knows target unit %s. Type known=%s, distance known=%s",self:GetName(),TargetUnit:GetName(),tostring(TypeKnown),tostring(DistanceKnown))) -Controller:knowTarget(object,TypeKnown,DistanceKnown) -end -end -end -end -end -function UNIT:GetTemplate() -local group=self:GetGroup() -local name=self:GetName() -if group then -local template=group:GetTemplate() -if template then -for _,unit in pairs(template.units)do -if unit.name==name then -return UTILS.DeepCopy(unit) -end -end -end -end -return nil -end -function UNIT:GetTemplatePayload() -local unit=self:GetTemplate() -if unit then -return unit.payload -end -return nil -end -function UNIT:GetTemplatePylons() -local payload=self:GetTemplatePayload() -if payload then -return payload.pylons -end -return nil -end -function UNIT:GetTemplateFuel() -local payload=self:GetTemplatePayload() -if payload then -return payload.fuel -end -return nil -end -function UNIT:EnableEmission(switch) -self:F2(self.UnitName) -local switch=switch or false -local DCSUnit=self:GetDCSObject() -if DCSUnit then -DCSUnit:enableEmission(switch) -end -end -CLIENT={ -ClassName="CLIENT", -ClientName=nil, -ClientAlive=false, -ClientTransport=false, -ClientBriefingShown=false, -_Menus={}, -_Tasks={}, -Messages={}, -Players={}, -} -function CLIENT:Find(DCSUnit,Error) -local ClientName=DCSUnit:getName() -local ClientFound=_DATABASE:FindClient(ClientName) -if ClientFound then -ClientFound:F(ClientName) -return ClientFound -end -if not Error then -error("CLIENT not found for: "..ClientName) -end -end -function CLIENT:FindByName(ClientName,ClientBriefing,Error) -local ClientFound=_DATABASE:FindClient(ClientName) -if ClientFound then -ClientFound:F({ClientName,ClientBriefing}) -ClientFound:AddBriefing(ClientBriefing) -ClientFound.MessageSwitch=true -return ClientFound -end -if not Error then -error("CLIENT not found for: "..ClientName) -end -end -function CLIENT:Register(ClientName) -local self=BASE:Inherit(self,UNIT:Register(ClientName)) -self.ClientName=ClientName -self.MessageSwitch=true -self.ClientAlive2=false -return self -end -function CLIENT:Transport() -self.ClientTransport=true -return self -end -function CLIENT:AddBriefing(ClientBriefing) -self.ClientBriefing=ClientBriefing -self.ClientBriefingShown=false -return self -end -function CLIENT:AddPlayer(PlayerName) -table.insert(self.Players,PlayerName) -return self -end -function CLIENT:GetPlayers() -return self.Players -end -function CLIENT:GetPlayer() -if#self.Players>0 then -return self.Players[1] -end -return nil -end -function CLIENT:RemovePlayer(PlayerName) -for i,playername in pairs(self.Players)do -if PlayerName==playername then -table.remove(self.Players,i) -break -end -end -return self -end -function CLIENT:RemovePlayers() -self.Players={} -return self -end -function CLIENT:ShowBriefing() -if not self.ClientBriefingShown then -self.ClientBriefingShown=true -local Briefing="" -if self.ClientBriefing and self.ClientBriefing~=""then -Briefing=Briefing..self.ClientBriefing -self:Message(Briefing,60,"Briefing") -end -end -return self -end -function CLIENT:ShowMissionBriefing(MissionBriefing) -self:F({self.ClientName}) -if MissionBriefing then -self:Message(MissionBriefing,60,"Mission Briefing") -end -return self -end -function CLIENT:Reset(ClientName) -self:F() -self._Menus={} -end -function CLIENT:IsMultiSeated() -self:F(self.ClientName) -local ClientMultiSeatedTypes={ -["Mi-8MT"]="Mi-8MT", -["UH-1H"]="UH-1H", -["P-51B"]="P-51B" -} -if self:IsAlive()then -local ClientTypeName=self:GetClientGroupUnit():GetTypeName() -if ClientMultiSeatedTypes[ClientTypeName]then -return true -end -end -return false -end -function CLIENT:Alive(CallBackFunction,...) -self:F() -self.ClientCallBack=CallBackFunction -self.ClientParameters=arg -self.AliveCheckScheduler=SCHEDULER:New(self,self._AliveCheckScheduler,{"Client Alive "..self.ClientName},0.1,5,0.5) -self.AliveCheckScheduler:NoTrace() -return self -end -function CLIENT:_AliveCheckScheduler(SchedulerName) -self:F3({SchedulerName,self.ClientName,self.ClientAlive2,self.ClientBriefingShown,self.ClientCallBack}) -if self:IsAlive()then -if self.ClientAlive2==false then -self:ShowBriefing() -if self.ClientCallBack then -self:T("Calling Callback function") -self.ClientCallBack(self,unpack(self.ClientParameters)) -end -self.ClientAlive2=true -end -else -if self.ClientAlive2==true then -self.ClientAlive2=false -end -end -return true -end -function CLIENT:GetDCSGroup() -self:F3() -local ClientUnit=Unit.getByName(self.ClientName) -local CoalitionsData={AlivePlayersRed=coalition.getPlayers(coalition.side.RED),AlivePlayersBlue=coalition.getPlayers(coalition.side.BLUE)} -for CoalitionId,CoalitionData in pairs(CoalitionsData)do -self:T3({"CoalitionData:",CoalitionData}) -for UnitId,UnitData in pairs(CoalitionData)do -self:T3({"UnitData:",UnitData}) -if UnitData and UnitData:isExist()then -if ClientUnit then -local ClientGroup=ClientUnit:getGroup() -if ClientGroup then -self:T3("ClientGroup = "..self.ClientName) -if ClientGroup:isExist()and UnitData:getGroup():isExist()then -if ClientGroup:getID()==UnitData:getGroup():getID()then -self:T3("Normal logic") -self:T3(self.ClientName.." : group found!") -self.ClientGroupID=ClientGroup:getID() -self.ClientGroupName=ClientGroup:getName() -return ClientGroup -end -else -self:T3("Bug 1.5 logic") -local ClientGroupTemplate=_DATABASE.Templates.Units[self.ClientName].GroupTemplate -self.ClientGroupID=ClientGroupTemplate.groupId -self.ClientGroupName=_DATABASE.Templates.Units[self.ClientName].GroupName -self:T3(self.ClientName.." : group found in bug 1.5 resolvement logic!") -return ClientGroup -end -end -else -end -end -end -end -if ClientUnit then -local ClientGroup=ClientUnit:getGroup() -if ClientGroup then -self:T3("ClientGroup = "..self.ClientName) -if ClientGroup:isExist()then -self:T3("Normal logic") -self:T3(self.ClientName.." : group found!") -return ClientGroup -end -end -end -self.ClientGroupID=nil -self.ClientGroupName=nil -return nil -end -function CLIENT:GetClientGroupID() -self:GetDCSGroup() -return self.ClientGroupID -end -function CLIENT:GetClientGroupName() -self:GetDCSGroup() -return self.ClientGroupName -end -function CLIENT:GetClientGroupUnit() -self:F2() -local ClientDCSUnit=Unit.getByName(self.ClientName) -self:T(self.ClientDCSUnit) -if ClientDCSUnit and ClientDCSUnit:isExist()then -local ClientUnit=_DATABASE:FindUnit(self.ClientName) -return ClientUnit -end -return nil -end -function CLIENT:GetClientGroupDCSUnit() -self:F2() -local ClientDCSUnit=Unit.getByName(self.ClientName) -if ClientDCSUnit and ClientDCSUnit:isExist()then -self:T2(ClientDCSUnit) -return ClientDCSUnit -end -end -function CLIENT:IsTransport() -self:F() -return self.ClientTransport -end -function CLIENT:ShowCargo() -self:F() -local CargoMsg="" -for CargoName,Cargo in pairs(CARGOS)do -if self==Cargo:IsLoadedInClient()then -CargoMsg=CargoMsg..Cargo.CargoName.." Type:"..Cargo.CargoType.." Weight: "..Cargo.CargoWeight.."\n" -end -end -if CargoMsg==""then -CargoMsg="empty" -end -self:Message(CargoMsg,15,"Co-Pilot: Cargo Status",30) -end -function CLIENT:Message(Message,MessageDuration,MessageCategory,MessageInterval,MessageID) -self:F({Message,MessageDuration,MessageCategory,MessageInterval}) -if self.MessageSwitch==true then -if MessageCategory==nil then -MessageCategory="Messages" -end -if MessageID~=nil then -if self.Messages[MessageID]==nil then -self.Messages[MessageID]={} -self.Messages[MessageID].MessageId=MessageID -self.Messages[MessageID].MessageTime=timer.getTime() -self.Messages[MessageID].MessageDuration=MessageDuration -if MessageInterval==nil then -self.Messages[MessageID].MessageInterval=600 -else -self.Messages[MessageID].MessageInterval=MessageInterval -end -MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self) -else -if self:GetClientGroupDCSUnit()and not self:GetClientGroupDCSUnit():inAir()then -if timer.getTime()-self.Messages[MessageID].MessageTime>=self.Messages[MessageID].MessageDuration+10 then -MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self) -self.Messages[MessageID].MessageTime=timer.getTime() -end -else -if timer.getTime()-self.Messages[MessageID].MessageTime>=self.Messages[MessageID].MessageDuration+self.Messages[MessageID].MessageInterval then -MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self) -self.Messages[MessageID].MessageTime=timer.getTime() -end -end -end -else -MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self) -end -end -end -STATIC={ -ClassName="STATIC", -} -function STATIC:Register(StaticName) -local self=BASE:Inherit(self,POSITIONABLE:New(StaticName)) -self.StaticName=StaticName -return self -end -function STATIC:Find(DCSStatic) -local StaticName=DCSStatic:getName() -local StaticFound=_DATABASE:FindStatic(StaticName) -return StaticFound -end -function STATIC:FindByName(StaticName,RaiseError) -local StaticFound=_DATABASE:FindStatic(StaticName) -self.StaticName=StaticName -if StaticFound then -return StaticFound -end -if RaiseError==nil or RaiseError==true then -error("STATIC not found for: "..StaticName) -end -return nil -end -function STATIC:Destroy(GenerateEvent) -self:F2(self.ObjectName) -local DCSObject=self:GetDCSObject() -if DCSObject then -local StaticName=DCSObject:getName() -self:F({StaticName=StaticName}) -if GenerateEvent and GenerateEvent==true then -if self:IsAir()then -self:CreateEventCrash(timer.getTime(),DCSObject) -else -self:CreateEventDead(timer.getTime(),DCSObject) -end -elseif GenerateEvent==false then -else -self:CreateEventRemoveUnit(timer.getTime(),DCSObject) -end -DCSObject:destroy() -return true -end -return nil -end -function STATIC:GetDCSObject() -local DCSStatic=StaticObject.getByName(self.StaticName) -if DCSStatic then -return DCSStatic -end -return nil -end -function STATIC:GetUnits() -self:F2({self.StaticName}) -local DCSStatic=self:GetDCSObject() -local Statics={} -if DCSStatic then -Statics[1]=STATIC:Find(DCSStatic) -self:T3(Statics) -return Statics -end -return nil -end -function STATIC:GetThreatLevel() -return 1,"Static" -end -function STATIC:SpawnAt(Coordinate,Heading,Delay) -Heading=Heading or 0 -if Delay and Delay>0 then -SCHEDULER:New(nil,self.SpawnAt,{self,Coordinate,Heading},Delay) -else -local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName) -SpawnStatic:SpawnFromPointVec2(Coordinate,Heading,self.StaticName) -end -return self -end -function STATIC:ReSpawn(CountryID,Delay) -if Delay and Delay>0 then -SCHEDULER:New(nil,self.ReSpawn,{self,CountryID},Delay) -else -CountryID=CountryID or self:GetCountry() -local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,CountryID) -SpawnStatic:Spawn(nil,self.StaticName) -end -return self -end -function STATIC:ReSpawnAt(Coordinate,Heading,Delay) -if Delay and Delay>0 then -SCHEDULER:New(nil,self.ReSpawnAt,{self,Coordinate,Heading},Delay) -else -local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,self:GetCountry()) -SpawnStatic:SpawnFromCoordinate(Coordinate,Heading,self.StaticName) -end -return self -end -AIRBASE={ -ClassName="AIRBASE", -CategoryName={ -[Airbase.Category.AIRDROME]="Airdrome", -[Airbase.Category.HELIPAD]="Helipad", -[Airbase.Category.SHIP]="Ship", -}, -activerwyno=nil, -} -AIRBASE.Caucasus={ -["Gelendzhik"]="Gelendzhik", -["Krasnodar_Pashkovsky"]="Krasnodar-Pashkovsky", -["Sukhumi_Babushara"]="Sukhumi-Babushara", -["Gudauta"]="Gudauta", -["Batumi"]="Batumi", -["Senaki_Kolkhi"]="Senaki-Kolkhi", -["Kobuleti"]="Kobuleti", -["Kutaisi"]="Kutaisi", -["Tbilisi_Lochini"]="Tbilisi-Lochini", -["Soganlug"]="Soganlug", -["Vaziani"]="Vaziani", -["Anapa_Vityazevo"]="Anapa-Vityazevo", -["Krasnodar_Center"]="Krasnodar-Center", -["Novorossiysk"]="Novorossiysk", -["Krymsk"]="Krymsk", -["Maykop_Khanskaya"]="Maykop-Khanskaya", -["Sochi_Adler"]="Sochi-Adler", -["Mineralnye_Vody"]="Mineralnye Vody", -["Nalchik"]="Nalchik", -["Mozdok"]="Mozdok", -["Beslan"]="Beslan", -} -AIRBASE.Nevada={ -["Creech_AFB"]="Creech AFB", -["Groom_Lake_AFB"]="Groom Lake AFB", -["McCarran_International_Airport"]="McCarran International Airport", -["Nellis_AFB"]="Nellis AFB", -["Beatty_Airport"]="Beatty Airport", -["Boulder_City_Airport"]="Boulder City Airport", -["Echo_Bay"]="Echo Bay", -["Henderson_Executive_Airport"]="Henderson Executive Airport", -["Jean_Airport"]="Jean Airport", -["Laughlin_Airport"]="Laughlin Airport", -["Lincoln_County"]="Lincoln County", -["Mesquite"]="Mesquite", -["Mina_Airport_3Q0"]="Mina Airport 3Q0", -["North_Las_Vegas"]="North Las Vegas", -["Pahute_Mesa_Airstrip"]="Pahute Mesa Airstrip", -["Tonopah_Airport"]="Tonopah Airport", -["Tonopah_Test_Range_Airfield"]="Tonopah Test Range Airfield", -} -AIRBASE.Normandy={ -["Saint_Pierre_du_Mont"]="Saint Pierre du Mont", -["Lignerolles"]="Lignerolles", -["Cretteville"]="Cretteville", -["Maupertus"]="Maupertus", -["Brucheville"]="Brucheville", -["Meautis"]="Meautis", -["Cricqueville_en_Bessin"]="Cricqueville-en-Bessin", -["Lessay"]="Lessay", -["Sainte_Laurent_sur_Mer"]="Sainte-Laurent-sur-Mer", -["Biniville"]="Biniville", -["Cardonville"]="Cardonville", -["Deux_Jumeaux"]="Deux Jumeaux", -["Chippelle"]="Chippelle", -["Beuzeville"]="Beuzeville", -["Azeville"]="Azeville", -["Picauville"]="Picauville", -["Le_Molay"]="Le Molay", -["Longues_sur_Mer"]="Longues-sur-Mer", -["Carpiquet"]="Carpiquet", -["Bazenville"]="Bazenville", -["Sainte_Croix_sur_Mer"]="Sainte-Croix-sur-Mer", -["Beny_sur_Mer"]="Beny-sur-Mer", -["Rucqueville"]="Rucqueville", -["Sommervieu"]="Sommervieu", -["Lantheuil"]="Lantheuil", -["Evreux"]="Evreux", -["Chailey"]="Chailey", -["Needs_Oar_Point"]="Needs Oar Point", -["Funtington"]="Funtington", -["Tangmere"]="Tangmere", -["Ford_AF"]="Ford_AF", -["Goulet"]="Goulet", -["Argentan"]="Argentan", -["Vrigny"]="Vrigny", -["Essay"]="Essay", -["Hauterive"]="Hauterive", -["Barville"]="Barville", -["Conches"]="Conches", -} -AIRBASE.PersianGulf={ -["Abu_Dhabi_International_Airport"]="Abu Dhabi Intl", -["Abu_Musa_Island_Airport"]="Abu Musa Island", -["Al_Ain_International_Airport"]="Al Ain Intl", -["Al_Bateen_Airport"]="Al-Bateen", -["Al_Dhafra_AB"]="Al Dhafra AFB", -["Al_Maktoum_Intl"]="Al Maktoum Intl", -["Al_Minhad_AB"]="Al Minhad AFB", -["Bandar_Abbas_Intl"]="Bandar Abbas Intl", -["Bandar_Lengeh"]="Bandar Lengeh", -["Bandar_e_Jask_airfield"]="Bandar-e-Jask", -["Dubai_Intl"]="Dubai Intl", -["Fujairah_Intl"]="Fujairah Intl", -["Havadarya"]="Havadarya", -["Jiroft_Airport"]="Jiroft", -["Kerman_Airport"]="Kerman", -["Khasab"]="Khasab", -["Kish_International_Airport"]="Kish Intl", -["Lar_Airbase"]="Lar", -["Lavan_Island_Airport"]="Lavan Island", -["Liwa_Airbase"]="Liwa AFB", -["Qeshm_Island"]="Qeshm Island", -["Ras_Al_Khaimah"]="Ras Al Khaimah Intl", -["Sas_Al_Nakheel_Airport"]="Sas Al Nakheel", -["Sharjah_Intl"]="Sharjah Intl", -["Shiraz_International_Airport"]="Shiraz Intl", -["Sir_Abu_Nuayr"]="Sir Abu Nuayr", -["Sirri_Island"]="Sirri Island", -["Tunb_Island_AFB"]="Tunb Island AFB", -["Tunb_Kochak"]="Tunb Kochak", -} -AIRBASE.TheChannel={ -["Abbeville_Drucat"]="Abbeville Drucat", -["Merville_Calonne"]="Merville Calonne", -["Saint_Omer_Longuenesse"]="Saint Omer Longuenesse", -["Dunkirk_Mardyck"]="Dunkirk Mardyck", -["Manston"]="Manston", -["Hawkinge"]="Hawkinge", -["Lympne"]="Lympne", -["Detling"]="Detling", -["High_Halden"]="High Halden", -} -AIRBASE.Syria={ -["Kuweires"]="Kuweires", -["Marj_Ruhayyil"]="Marj Ruhayyil", -["Kiryat_Shmona"]="Kiryat Shmona", -["Marj_as_Sultan_North"]="Marj as Sultan North", -["Eyn_Shemer"]="Eyn Shemer", -["Incirlik"]="Incirlik", -["Damascus"]="Damascus", -["Bassel_Al_Assad"]="Bassel Al-Assad", -["Aleppo"]="Aleppo", -["Qabr_as_Sitt"]="Qabr as Sitt", -["Wujah_Al_Hajar"]="Wujah Al Hajar", -["Al_Dumayr"]="Al-Dumayr", -["Hatay"]="Hatay", -["Haifa"]="Haifa", -["Khalkhalah"]="Khalkhalah", -["Megiddo"]="Megiddo", -["Rayak"]="Rayak", -["Mezzeh"]="Mezzeh", -["King_Hussein_Air_College"]="King Hussein Air College", -["Jirah"]="Jirah", -["Taftanaz"]="Taftanaz", -["Rene_Mouawad"]="Rene Mouawad", -["Ramat_David"]="Ramat David", -["Minakh"]="Minakh", -["Adana_Sakirpasa"]="Adana Sakirpasa", -["Marj_as_Sultan_South"]="Marj as Sultan South", -["Hama"]="Hama", -["Al_Qusayr"]="Al Qusayr", -["Palmyra"]="Palmyra", -["Tabqa"]="Tabqa", -["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri", -["An_Nasiriyah"]="An Nasiriyah", -["Abu_al_Duhur"]="Abu al-Duhur", -["H4"]="H4", -["Gaziantep"]="Gaziantep", -["Rosh_Pina"]="Rosh Pina", -["Sayqal"]="Sayqal", -["Shayrat"]="Shayrat", -["Tiyas"]="Tiyas", -["Tha_lah"]="Tha'lah", -["Naqoura"]="Naqoura", -} -AIRBASE.TerminalType={ -Runway=16, -HelicopterOnly=40, -Shelter=68, -OpenMed=72, -OpenBig=104, -OpenMedOrBig=176, -HelicopterUsable=216, -FighterAircraft=244, -} -function AIRBASE:Register(AirbaseName) -local self=BASE:Inherit(self,POSITIONABLE:New(AirbaseName)) -self.AirbaseName=AirbaseName -self.AirbaseID=self:GetID(true) -self.descriptors=self:GetDesc() -self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME -if self.category==Airbase.Category.AIRDROME then -self.isAirdrome=true -elseif self.category==Airbase.Category.HELIPAD then -self.isHelipad=true -elseif self.category==Airbase.Category.SHIP then -self.isShip=true -else -self:E("ERROR: Unknown airbase category!") -end -self:_InitParkingSpots() -local vec2=self:GetVec2() -self:GetCoordinate() -if vec2 then -if self.isShip then -local unit=UNIT:FindByName(AirbaseName) -if unit then -self.AirbaseZone=ZONE_UNIT:New(AirbaseName,unit,2500) -end -else -self.AirbaseZone=ZONE_RADIUS:New(AirbaseName,vec2,2500) -end -else -self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s",AirbaseName)) -end -return self -end -function AIRBASE:Find(DCSAirbase) -local AirbaseName=DCSAirbase:getName() -local AirbaseFound=_DATABASE:FindAirbase(AirbaseName) -return AirbaseFound -end -function AIRBASE:FindByName(AirbaseName) -local AirbaseFound=_DATABASE:FindAirbase(AirbaseName) -return AirbaseFound -end -function AIRBASE:FindByID(id) -for name,_airbase in pairs(_DATABASE.AIRBASES)do -local airbase=_airbase -local aid=tonumber(airbase:GetID(true)) -if aid==id then -return airbase -end -end -return nil -end -function AIRBASE:GetDCSObject() -local DCSAirbase=Airbase.getByName(self.AirbaseName) -if DCSAirbase then -return DCSAirbase -end -return nil -end -function AIRBASE:GetZone() -return self.AirbaseZone -end -function AIRBASE.GetAllAirbases(coalition,category) -local airbases={} -for _,_airbase in pairs(_DATABASE.AIRBASES)do -local airbase=_airbase -if coalition==nil or airbase:GetCoalition()==coalition then -if category==nil or category==airbase:GetAirbaseCategory()then -table.insert(airbases,airbase) -end -end -end -return airbases -end -function AIRBASE.GetAllAirbaseNames(coalition,category) -local airbases={} -for airbasename,_airbase in pairs(_DATABASE.AIRBASES)do -local airbase=_airbase -if coalition==nil or airbase:GetCoalition()==coalition then -if category==nil or category==airbase:GetAirbaseCategory()then -table.insert(airbases,airbasename) -end -end -end -return airbases -end -function AIRBASE:GetID(unique) -if self.AirbaseID then -return unique and self.AirbaseID or math.abs(self.AirbaseID) -else -for DCSAirbaseId,DCSAirbase in ipairs(world.getAirbases())do -local AirbaseName=DCSAirbase:getName() -local airbaseID=tonumber(DCSAirbase:getID()) -local airbaseCategory=self:GetAirbaseCategory() -if AirbaseName==self.AirbaseName then -if airbaseCategory==Airbase.Category.SHIP or airbaseCategory==Airbase.Category.HELIPAD then -return unique and-airbaseID or airbaseID -else -return airbaseID -end -end -end -end -return nil -end -function AIRBASE:SetParkingSpotWhitelist(TerminalIdWhitelist) -if TerminalIdWhitelist==nil then -self.parkingWhitelist={} -return self -end -if type(TerminalIdWhitelist)~="table"then -TerminalIdWhitelist={TerminalIdWhitelist} -end -self.parkingWhitelist=TerminalIdWhitelist -return self -end -function AIRBASE:SetParkingSpotBlacklist(TerminalIdBlacklist) -if TerminalIdBlacklist==nil then -self.parkingBlacklist={} -return self -end -if type(TerminalIdBlacklist)~="table"then -TerminalIdBlacklist={TerminalIdBlacklist} -end -self.parkingBlacklist=TerminalIdBlacklist -return self -end -function AIRBASE:GetAirbaseCategory() -return self.category -end -function AIRBASE:IsAirdrome() -return self.isAirdrome -end -function AIRBASE:IsHelipad() -return self.isHelipad -end -function AIRBASE:IsShip() -return self.isShip -end -function AIRBASE:GetParkingData(available) -self:F2(available) -local DCSAirbase=self:GetDCSObject() -local parkingdata=nil -if DCSAirbase then -parkingdata=DCSAirbase:getParking(available) -end -self:T2({parkingdata=parkingdata}) -return parkingdata -end -function AIRBASE:GetParkingSpotsNumber(termtype) -local parkingdata=self:GetParkingData(false) -local nspots=0 -for _,parkingspot in pairs(parkingdata)do -if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then -nspots=nspots+1 -end -end -return nspots -end -function AIRBASE:GetFreeParkingSpotsNumber(termtype,allowTOAC) -local parkingdata=self:GetParkingData(true) -local nfree=0 -for _,parkingspot in pairs(parkingdata)do -if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then -if(allowTOAC and allowTOAC==true)or parkingspot.TO_AC==false then -nfree=nfree+1 -end -end -end -return nfree -end -function AIRBASE:GetFreeParkingSpotsCoordinates(termtype,allowTOAC) -local parkingdata=self:GetParkingData(true) -local spots={} -for _,parkingspot in pairs(parkingdata)do -if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then -if(allowTOAC and allowTOAC==true)or parkingspot.TO_AC==false then -table.insert(spots,COORDINATE:NewFromVec3(parkingspot.vTerminalPos)) -end -end -end -return spots -end -function AIRBASE:GetParkingSpotsCoordinates(termtype) -local parkingdata=self:GetParkingData(false) -local spots={} -for _,parkingspot in ipairs(parkingdata)do -if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then -local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos) -table.insert(spots,_coord) -end -end -return spots -end -function AIRBASE:_InitParkingSpots() -local parkingdata=self:GetParkingData(false) -self.parking={} -self.parkingByID={} -self.NparkingTotal=0 -self.NparkingTerminal={} -for _,terminalType in pairs(AIRBASE.TerminalType)do -self.NparkingTerminal[terminalType]=0 -end -for _,spot in pairs(parkingdata)do -local park={} -park.Vec3=spot.vTerminalPos -park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos) -park.DistToRwy=spot.fDistToRW -park.Free=nil -park.TerminalID=spot.Term_Index -park.TerminalID0=spot.Term_Index_0 -park.TerminalType=spot.Term_Type -park.TOAC=spot.TO_AC -self.NparkingTotal=self.NparkingTotal+1 -for _,terminalType in pairs(AIRBASE.TerminalType)do -if self._CheckTerminalType(terminalType,park.TerminalType)then -self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1 -end -end -self.parkingByID[park.TerminalID]=park -table.insert(self.parking,park) -end -return self -end -function AIRBASE:_GetParkingSpotByID(TerminalID) -return self.parkingByID[TerminalID] -end -function AIRBASE:GetParkingSpotsTable(termtype) -local parkingdata=self:GetParkingData(false) -local parkingfree=self:GetParkingData(true) -local function _isfree(_tocheck) -for _,_spot in pairs(parkingfree)do -if _spot.Term_Index==_tocheck.Term_Index then -return true -end -end -return false -end -local spots={} -for _,_spot in pairs(parkingdata)do -if AIRBASE._CheckTerminalType(_spot.Term_Type,termtype)then -local spot=self:_GetParkingSpotByID(_spot.Term_Index) -if spot then -spot.Free=_isfree(_spot) -spot.TOAC=_spot.TO_AC -table.insert(spots,spot) -else -self:E(string.format("ERROR: Parking spot %s is nil!",tostring(_spot.Term_Index))) -end -end -end -return spots -end -function AIRBASE:GetFreeParkingSpotsTable(termtype,allowTOAC) -local parkingfree=self:GetParkingData(true) -local freespots={} -for _,_spot in pairs(parkingfree)do -if AIRBASE._CheckTerminalType(_spot.Term_Type,termtype)and _spot.Term_Index>0 then -if(allowTOAC and allowTOAC==true)or _spot.TO_AC==false then -local spot=self:_GetParkingSpotByID(_spot.Term_Index) -spot.Free=true -spot.TOAC=_spot.TO_AC -table.insert(freespots,spot) -end -end -end -return freespots -end -function AIRBASE:GetParkingSpotData(TerminalID) -local parkingdata=self:GetParkingSpotsTable() -for _,_spot in pairs(parkingdata)do -local spot=_spot -self:T({TerminalID=spot.TerminalID,TerminalType=spot.TerminalType}) -if TerminalID==spot.TerminalID then -return spot -end -end -self:E("ERROR: Could not find spot with Terminal ID="..tostring(TerminalID)) -return nil -end -function AIRBASE:MarkParkingSpots(termtype,mark) -if mark==nil then -mark=true -end -local parkingdata=self:GetParkingSpotsTable(termtype) -local airbasename=self:GetName() -self:E(string.format("Parking spots at %s for termial type %s:",airbasename,tostring(termtype))) -for _,_spot in pairs(parkingdata)do -local _text=string.format("Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m", -_spot.TerminalID,_spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy) -if mark then -_spot.Coordinate:MarkToAll(_text) -end -local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m", -airbasename,_spot.TerminalID,_spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy) -self:E(_text) -end -end -function AIRBASE:FindFreeParkingSpotForAircraft(group,terminaltype,scanradius,scanunits,scanstatics,scanscenery,verysafe,nspots,parkingdata) -scanradius=scanradius or 50 -if scanunits==nil then -scanunits=true -end -if scanstatics==nil then -scanstatics=true -end -if scanscenery==nil then -scanscenery=false -end -if verysafe==nil then -verysafe=false -end -local function _overlap(object1,object2,dist) -local pos1=object1 -local pos2=object2 -local r1=pos1:GetBoundingRadius() -local r2=pos2:GetBoundingRadius() -if r1 and r2 then -local safedist=(r1+r2)*1.1 -local safe=(dist>safedist) -self:T2(string.format("r1=%.1f r2=%.1f s=%.1f d=%.1f ==> safe=%s",r1,r2,safedist,dist,tostring(safe))) -return safe -else -return true -end -end -local airport=self:GetName() -parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype) -local aircraft=group:GetUnit(1) -local _aircraftsize,ax,ay,az=aircraft:GetObjectSize() -local _nspots=nspots or group:GetSize() -self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.",airport,_nspots,_aircraftsize,ax,ay,az,tostring(terminaltype))) -local validspots={} -local nvalid=0 -local _test=false -if _test then -return validspots -end -local markobstacles=false -for _,parkingspot in pairs(parkingdata)do -local _spot=parkingspot.Coordinate -local _termid=parkingspot.TerminalID -if AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype)and self:_CheckParkingLists(_termid)then -if verysafe and(parkingspot.Free==false or parkingspot.TOAC==true)then -self:T(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet). Free=%s, TOAC=%s.",airport,parkingspot.TerminalID,tostring(parkingspot.Free),tostring(parkingspot.TOAC))) -else -local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius,scanunits,scanstatics,scanscenery) -local occupied=false -for _,unit in pairs(_units)do -local _coord=unit:GetCoordinate() -local _dist=_coord:Get2DDistance(_spot) -local _safe=_overlap(aircraft,unit,_dist) -if markobstacles then -local l,x,y,z=unit:GetObjectSize() -_coord:MarkToAll(string.format("Unit %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s",unit:GetName(),x,y,z,l,_dist,_termid,tostring(_safe))) -end -if scanunits and not _safe then -occupied=true -end -end -for _,static in pairs(_statics)do -local _static=STATIC:Find(static) -local _vec3=static:getPoint() -local _coord=COORDINATE:NewFromVec3(_vec3) -local _dist=_coord:Get2DDistance(_spot) -local _safe=_overlap(aircraft,_static,_dist) -if markobstacles then -local l,x,y,z=_static:GetObjectSize() -_coord:MarkToAll(string.format("Static %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s",static:getName(),x,y,z,l,_dist,_termid,tostring(_safe))) -end -if scanstatics and not _safe then -occupied=true -end -end -for _,scenery in pairs(_sceneries)do -local _scenery=SCENERY:Register(scenery:getTypeName(),scenery) -local _vec3=scenery:getPoint() -local _coord=COORDINATE:NewFromVec3(_vec3) -local _dist=_coord:Get2DDistance(_spot) -local _safe=_overlap(aircraft,_scenery,_dist) -if markobstacles then -local l,x,y,z=scenery:GetObjectSize(scenery) -_coord:MarkToAll(string.format("Scenery %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s",scenery:getTypeName(),x,y,z,l,_dist,_termid,tostring(_safe))) -end -if scanscenery and not _safe then -occupied=true -end -end -for _,_takenspot in pairs(validspots)do -local _dist=_takenspot.Coordinate:Get2DDistance(_spot) -local _safe=_overlap(aircraft,aircraft,_dist) -if not _safe then -occupied=true -end -end -if occupied then -self:I(string.format("%s: Parking spot id %d occupied.",airport,_termid)) -else -self:I(string.format("%s: Parking spot id %d free.",airport,_termid)) -if nvalid<_nspots then -table.insert(validspots,{Coordinate=_spot,TerminalID=_termid}) -end -nvalid=nvalid+1 -self:I(string.format("%s: Parking spot id %d free. Nfree=%d/%d.",airport,_termid,nvalid,_nspots)) -end -end -if nvalid>=_nspots then -return validspots -end -end -end -return validspots -end -function AIRBASE:_CheckParkingLists(TerminalID) -if self.parkingBlacklist and#self.parkingBlacklist>0 then -for _,terminalID in pairs(self.parkingBlacklist or{})do -if terminalID==TerminalID then -return false -end -end -end -if self.parkingWhitelist and#self.parkingWhitelist>0 then -for _,terminalID in pairs(self.parkingWhitelist or{})do -if terminalID==TerminalID then -return true -end -end -return false -end -return true -end -function AIRBASE._CheckTerminalType(Term_Type,termtype) -if Term_Type==nil then -return false -end -if termtype==nil then -if Term_Type==AIRBASE.TerminalType.Runway then -return false -else -return true -end -end -local match=false -if Term_Type==termtype then -match=true -end -if termtype==AIRBASE.TerminalType.OpenMedOrBig then -if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig then -match=true -end -elseif termtype==AIRBASE.TerminalType.HelicopterUsable then -if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.HelicopterOnly then -match=true -end -elseif termtype==AIRBASE.TerminalType.FighterAircraft then -if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter then -match=true -end -end -return match -end -function AIRBASE:GetRunwayData(magvar,mark) -local runways={} -if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then -return{} -end -local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway) -if false then -for i,_coord in pairs(runwaycoords)do -local coord=_coord -coord:Translate(100,0):MarkToAll("Runway i="..i) -end -end -magvar=magvar or UTILS.GetMagneticDeclination() -local N=#runwaycoords -local N2=N/2 -local exception=false -local name=self:GetName() -if name==AIRBASE.Nevada.Jean_Airport or -name==AIRBASE.Nevada.Creech_AFB or -name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or -name==AIRBASE.PersianGulf.Dubai_Intl or -name==AIRBASE.PersianGulf.Shiraz_International_Airport or -name==AIRBASE.PersianGulf.Kish_International_Airport then -exception=1 -elseif UTILS.GetDCSMap()==DCSMAP.Syria and N>=2 and -name~=AIRBASE.Syria.Minakh and -name~=AIRBASE.Syria.Damascus and -name~=AIRBASE.Syria.Khalkhalah and -name~=AIRBASE.Syria.Marj_Ruhayyil and -name~=AIRBASE.Syria.Beirut_Rafic_Hariri then -exception=2 -end -local function f(i) -local j -if exception==1 then -j=N-(i-1) -elseif exception==2 then -if i<=N2 then -j=i+N2 -else -j=i-N2 -end -else -if i%2==0 then -j=i-1 -else -j=i+1 -end -end -if name==AIRBASE.Syria.Beirut_Rafic_Hariri then -if i==1 then -j=3 -elseif i==2 then -j=6 -elseif i==3 then -j=1 -elseif i==4 then -j=5 -elseif i==5 then -j=4 -elseif i==6 then -j=2 -end -end -if name==AIRBASE.Syria.Ramat_David then -if i==1 then -j=4 -elseif i==2 then -j=6 -elseif i==3 then -j=5 -elseif i==4 then -j=1 -elseif i==5 then -j=3 -elseif i==6 then -j=2 -end -end -return j -end -for i=1,N do -local j=f(i) -local c1=runwaycoords[i] -local c2=runwaycoords[j] -local hdg=c1:HeadingTo(c2) -local idx=string.format("%02d",UTILS.Round((hdg-magvar)/10,0)) -local runway={} -runway.heading=hdg -runway.idx=idx -runway.length=c1:Get2DDistance(c2) -runway.position=c1 -runway.endpoint=c2 -if mark then -runway.position:MarkToAll(string.format("Runway %s: true heading=%03d (magvar=%d), length=%d m, i=%d, j=%d",runway.idx,runway.heading,magvar,runway.length,i,j)) -end -table.insert(runways,runway) -end -return runways -end -function AIRBASE:SetActiveRunway(iactive) -self.activerwyno=iactive -end -function AIRBASE:GetActiveRunway(magvar) -local runways=self:GetRunwayData(magvar) -if self.activerwyno then -return runways[self.activerwyno] -end -local Vwind=self:GetCoordinate():GetWindWithTurbulenceVec3() -local norm=UTILS.VecNorm(Vwind) -local iact=1 -if norm>0 then -Vwind.x=Vwind.x/norm -Vwind.y=0 -Vwind.z=Vwind.z/norm -local dotmin=nil -for i,_runway in pairs(runways)do -local runway=_runway -local alpha=math.rad(runway.heading) -local Vrunway={x=math.cos(alpha),y=0,z=math.sin(alpha)} -local dot=UTILS.VecDot(Vwind,Vrunway) -if dotmin==nil or dot radius %.1f m. Despawn = %s.",self:GetName(),unit:GetName(),group:GetName(),_i,dist,radius,tostring(despawn))) -end -end -else -self:T(string.format("%s, checking if unit %s of group %s is on runway. Unit is NOT alive.",self:GetName(),unit:GetName(),group:GetName())) -end -end -else -self:T(string.format("%s, checking if group %s is on runway. Group is NOT alive.",self:GetName(),group:GetName())) -end -return false -end -SCENERY={ -ClassName="SCENERY", -} -function SCENERY:Register(SceneryName,SceneryObject) -local self=BASE:Inherit(self,POSITIONABLE:New(SceneryName)) -self.SceneryName=SceneryName -self.SceneryObject=SceneryObject -return self -end -function SCENERY:GetDCSObject() -return self.SceneryObject -end -function SCENERY:GetThreatLevel() -return 0,"Scenery" -end -function SCENERY:FindByName(name) -local findAirbase=function() -local airbases=AIRBASE.GetAllAirbases() -for index,airbase in pairs(airbases)do -local surftype=airbase:GetCoordinate():GetSurfaceType() -if surftype~=land.SurfaceType.SHALLOW_WATER and surftype~=land.SurfaceType.WATER then -return airbase:GetCoordinate() -end -end -return nil -end -local sceneryScan=function(scancoord) -if scancoord~=nil then -local _,_,sceneryfound,_,_,scenerylist=scancoord:ScanObjects(200,false,false,true) -if sceneryfound==true then -scenerylist[1].id_=name -SCENERY.SceneryObject=SCENERY:Register(scenerylist[1].id_,scenerylist[1]) -return SCENERY.SceneryObject -end -end -return nil -end -if SCENERY.SceneryObject then -SCENERY.SceneryObject.SceneryObject.id_=name -SCENERY.SceneryObject.SceneryName=name -return SCENERY:Register(SCENERY.SceneryObject.SceneryObject.id_,SCENERY.SceneryObject.SceneryObject) -else -return sceneryScan(findAirbase()) -end -end -MARKER={ -ClassName="MARKER", -Debug=false, -lid=nil, -mid=nil, -coordinate=nil, -text=nil, -message=nil, -readonly=nil, -coalition=nil, -} -_MARKERID=0 -MARKER.version="0.1.0" -function MARKER:New(Coordinate,Text) -local self=BASE:Inherit(self,FSM:New()) -self.coordinate=Coordinate -self.text=Text -self.readonly=false -self.message="" -_MARKERID=_MARKERID+1 -self.myid=_MARKERID -self.lid=string.format("Marker #%d | ",self.myid) -self:SetStartState("Invisible") -self:AddTransition("Invisible","Added","Visible") -self:AddTransition("Visible","Removed","Invisible") -self:AddTransition("*","Changed","*") -self:AddTransition("*","TextUpdate","*") -self:AddTransition("*","CoordUpdate","*") -self:HandleEvent(EVENTS.MarkAdded) -self:HandleEvent(EVENTS.MarkRemoved) -self:HandleEvent(EVENTS.MarkChange) -return self -end -function MARKER:ReadOnly() -self.readonly=true -return self -end -function MARKER:Message(Text) -self.message=Text or"" -return self -end -function MARKER:ToAll(Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MARKER.ToAll,self) -else -self.toall=true -self.tocoaliton=nil -self.coalition=nil -self.togroup=nil -self.groupname=nil -self.groupid=nil -if self.shown then -self:Remove() -end -self.mid=UTILS.GetMarkID() -trigger.action.markToAll(self.mid,self.text,self.coordinate:GetVec3(),self.readonly,self.message) -end -return self -end -function MARKER:ToCoalition(Coalition,Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MARKER.ToCoalition,self,Coalition) -else -self.coalition=Coalition -self.tocoaliton=true -self.toall=false -self.togroup=false -self.groupname=nil -self.groupid=nil -if self.shown then -self:Remove() -end -self.mid=UTILS.GetMarkID() -trigger.action.markToCoalition(self.mid,self.text,self.coordinate:GetVec3(),self.coalition,self.readonly,self.message) -end -return self -end -function MARKER:ToBlue(Delay) -self:ToCoalition(coalition.side.BLUE,Delay) -return self -end -function MARKER:ToRed(Delay) -self:ToCoalition(coalition.side.RED,Delay) -return self -end -function MARKER:ToNeutral(Delay) -self:ToCoalition(coalition.side.NEUTRAL,Delay) -return self -end -function MARKER:ToGroup(Group,Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MARKER.ToGroup,self,Group) -else -if Group and Group:IsAlive()~=nil then -self.groupid=Group:GetID() -if self.groupid then -self.groupname=Group:GetName() -self.togroup=true -self.tocoaliton=nil -self.coalition=nil -self.toall=nil -if self.shown then -self:Remove() -end -self.mid=UTILS.GetMarkID() -trigger.action.markToGroup(self.mid,self.text,self.coordinate:GetVec3(),self.groupid,self.readonly,self.message) -end -else -end -end -return self -end -function MARKER:UpdateText(Text,Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MARKER.UpdateText,self,Text) -else -self.text=tostring(Text) -self:Refresh() -self:TextUpdate(tostring(Text)) -end -return self -end -function MARKER:UpdateCoordinate(Coordinate,Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MARKER.UpdateCoordinate,self,Coordinate) -else -self.coordinate=Coordinate -self:Refresh() -self:CoordUpdate(Coordinate) -end -return self -end -function MARKER:Refresh(Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MARKER.Refresh,self) -else -if self.toall then -self:ToAll() -elseif self.tocoaliton then -self:ToCoalition(self.coalition) -elseif self.togroup then -local group=GROUP:FindByName(self.groupname) -self:ToGroup(group) -else -self:E(self.lid.."ERROR: unknown To in :Refresh()!") -end -end -return self -end -function MARKER:Remove(Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MARKER.Remove,self) -else -if self.shown then -trigger.action.removeMark(self.mid) -end -end -return self -end -function MARKER:GetCoordinate() -return self.coordinate -end -function MARKER:GetText() -return self.text -end -function MARKER:SetText(Text) -self.text=Text and tostring(Text)or"" -return self -end -function MARKER:IsVisible() -return self:Is("Visible") -end -function MARKER:IsInvisible() -return self:Is("Invisible") -end -function MARKER:OnEventMarkAdded(EventData) -if EventData and EventData.MarkID then -local MarkID=EventData.MarkID -self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s",tostring(MarkID))) -if MarkID==self.mid then -self.shown=true -self:Added(EventData) -end -end -end -function MARKER:OnEventMarkRemoved(EventData) -if EventData and EventData.MarkID then -local MarkID=EventData.MarkID -self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s",tostring(MarkID))) -if MarkID==self.mid then -self.shown=false -self:Removed(EventData) -end -end -end -function MARKER:OnEventMarkChange(EventData) -if EventData and EventData.MarkID then -local MarkID=EventData.MarkID -self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s",tostring(MarkID))) -if MarkID==self.mid then -self:Changed(EventData) -self:TextChanged(tostring(EventData.MarkText)) -end -end -end -function MARKER:onafterAdded(From,Event,To,EventData) -local text=string.format("Captured event MarkAdded for myself:\n") -text=text..string.format("Marker ID = %s\n",tostring(EventData.MarkID)) -text=text..string.format("Coalition = %s\n",tostring(EventData.MarkCoalition)) -text=text..string.format("Group ID = %s\n",tostring(EventData.MarkGroupID)) -text=text..string.format("Initiator = %s\n",EventData.IniUnit and EventData.IniUnit:GetName()or"Nobody") -text=text..string.format("Coordinate = %s\n",EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS()or"Nowhere") -text=text..string.format("Text: \n%s",tostring(EventData.MarkText)) -self:T2(self.lid..text) -end -function MARKER:onafterRemoved(From,Event,To,EventData) -local text=string.format("Captured event MarkRemoved for myself:\n") -text=text..string.format("Marker ID = %s\n",tostring(EventData.MarkID)) -text=text..string.format("Coalition = %s\n",tostring(EventData.MarkCoalition)) -text=text..string.format("Group ID = %s\n",tostring(EventData.MarkGroupID)) -text=text..string.format("Initiator = %s\n",EventData.IniUnit and EventData.IniUnit:GetName()or"Nobody") -text=text..string.format("Coordinate = %s\n",EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS()or"Nowhere") -text=text..string.format("Text: \n%s",tostring(EventData.MarkText)) -self:T2(self.lid..text) -end -function MARKER:onafterChanged(From,Event,To,EventData) -local text=string.format("Captured event MarkChange for myself:\n") -text=text..string.format("Marker ID = %s\n",tostring(EventData.MarkID)) -text=text..string.format("Coalition = %s\n",tostring(EventData.MarkCoalition)) -text=text..string.format("Group ID = %s\n",tostring(EventData.MarkGroupID)) -text=text..string.format("Initiator = %s\n",EventData.IniUnit and EventData.IniUnit:GetName()or"Nobody") -text=text..string.format("Coordinate = %s\n",EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS()or"Nowhere") -text=text..string.format("Text: \n%s",tostring(EventData.MarkText)) -self:T2(self.lid..text) -end -function MARKER:onafterTextUpdate(From,Event,To,Text) -self:T(self.lid..string.format("New Marker Text:\n%s",Text)) -end -function MARKER:onafterCoordUpdate(From,Event,To,Coordinate) -self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s",Coordinate:ToStringLLDMS())) -end -CARGOS={} -do -CARGO={ -ClassName="CARGO", -Type=nil, -Name=nil, -Weight=nil, -CargoObject=nil, -CargoCarrier=nil, -Representable=false, -Slingloadable=false, -Moveable=false, -Containable=false, -Reported={}, -} -function CARGO:New(Type,Name,Weight,LoadRadius,NearRadius) -local self=BASE:Inherit(self,FSM:New()) -self:F({Type,Name,Weight,LoadRadius,NearRadius}) -self:SetStartState("UnLoaded") -self:AddTransition({"UnLoaded","Boarding"},"Board","Boarding") -self:AddTransition("Boarding","Boarding","Boarding") -self:AddTransition("Boarding","CancelBoarding","UnLoaded") -self:AddTransition("Boarding","Load","Loaded") -self:AddTransition("UnLoaded","Load","Loaded") -self:AddTransition("Loaded","UnBoard","UnBoarding") -self:AddTransition("UnBoarding","UnBoarding","UnBoarding") -self:AddTransition("UnBoarding","UnLoad","UnLoaded") -self:AddTransition("Loaded","UnLoad","UnLoaded") -self:AddTransition("*","Damaged","Damaged") -self:AddTransition("*","Destroyed","Destroyed") -self:AddTransition("*","Respawn","UnLoaded") -self:AddTransition("*","Reset","UnLoaded") -self.Type=Type -self.Name=Name -self.Weight=Weight or 0 -self.CargoObject=nil -self.CargoCarrier=nil -self.Representable=false -self.Slingloadable=false -self.Moveable=false -self.Containable=false -self.CargoLimit=0 -self.LoadRadius=LoadRadius or 500 -self:SetDeployed(false) -self.CargoScheduler=SCHEDULER:New() -CARGOS[self.Name]=self -return self -end -function CARGO:FindByName(CargoName) -local CargoFound=_DATABASE:FindCargo(CargoName) -return CargoFound -end -function CARGO:GetX() -if self:IsLoaded()then -return self.CargoCarrier:GetCoordinate().x -else -return self.CargoObject:GetCoordinate().x -end -end -function CARGO:GetY() -if self:IsLoaded()then -return self.CargoCarrier:GetCoordinate().z -else -return self.CargoObject:GetCoordinate().z -end -end -function CARGO:GetHeading() -if self:IsLoaded()then -return self.CargoCarrier:GetHeading() -else -return self.CargoObject:GetHeading() -end -end -function CARGO:CanSlingload() -return false -end -function CARGO:CanBoard() -return true -end -function CARGO:CanUnboard() -return true -end -function CARGO:CanLoad() -return true -end -function CARGO:CanUnload() -return true -end -function CARGO:Destroy() -if self.CargoObject then -self.CargoObject:Destroy() -end -self:Destroyed() -end -function CARGO:GetName() -return self.Name -end -function CARGO:GetObject() -if self:IsLoaded()then -return self.CargoCarrier -else -return self.CargoObject -end -end -function CARGO:GetObjectName() -if self:IsLoaded()then -return self.CargoCarrier:GetName() -else -return self.CargoObject:GetName() -end -end -function CARGO:GetCount() -return 1 -end -function CARGO:GetType() -return self.Type -end -function CARGO:GetTransportationMethod() -return self.TransportationMethod -end -function CARGO:GetCoalition() -if self:IsLoaded()then -return self.CargoCarrier:GetCoalition() -else -return self.CargoObject:GetCoalition() -end -end -function CARGO:GetCoordinate() -return self.CargoObject:GetCoordinate() -end -function CARGO:IsDestroyed() -return self:Is("Destroyed") -end -function CARGO:IsLoaded() -return self:Is("Loaded") -end -function CARGO:IsLoadedInCarrier(Carrier) -return self.CargoCarrier and self.CargoCarrier:GetName()==Carrier:GetName() -end -function CARGO:IsUnLoaded() -return self:Is("UnLoaded") -end -function CARGO:IsBoarding() -return self:Is("Boarding") -end -function CARGO:IsUnboarding() -return self:Is("UnBoarding") -end -function CARGO:IsAlive() -if self:IsLoaded()then -return self.CargoCarrier:IsAlive() -else -return self.CargoObject:IsAlive() -end -end -function CARGO:SetDeployed(Deployed) -self.Deployed=Deployed -end -function CARGO:IsDeployed() -return self.Deployed -end -function CARGO:Spawn(PointVec2) -self:F() -end -function CARGO:Flare(FlareColor) -if self:IsUnLoaded()then -trigger.action.signalFlare(self.CargoObject:GetVec3(),FlareColor,0) -end -end -function CARGO:FlareWhite() -self:Flare(trigger.flareColor.White) -end -function CARGO:FlareYellow() -self:Flare(trigger.flareColor.Yellow) -end -function CARGO:FlareGreen() -self:Flare(trigger.flareColor.Green) -end -function CARGO:FlareRed() -self:Flare(trigger.flareColor.Red) -end -function CARGO:Smoke(SmokeColor,Radius) -if self:IsUnLoaded()then -if Radius then -trigger.action.smoke(self.CargoObject:GetRandomVec3(Radius),SmokeColor) -else -trigger.action.smoke(self.CargoObject:GetVec3(),SmokeColor) -end -end -end -function CARGO:SmokeGreen() -self:Smoke(trigger.smokeColor.Green,Range) -end -function CARGO:SmokeRed() -self:Smoke(trigger.smokeColor.Red,Range) -end -function CARGO:SmokeWhite() -self:Smoke(trigger.smokeColor.White,Range) -end -function CARGO:SmokeOrange() -self:Smoke(trigger.smokeColor.Orange,Range) -end -function CARGO:SmokeBlue() -self:Smoke(trigger.smokeColor.Blue,Range) -end -function CARGO:SetLoadRadius(LoadRadius) -self.LoadRadius=LoadRadius or 150 -end -function CARGO:GetLoadRadius() -return self.LoadRadius -end -function CARGO:IsInLoadRadius(Coordinate) -self:F({Coordinate,LoadRadius=self.LoadRadius}) -local Distance=0 -if self:IsUnLoaded()then -local CargoCoordinate=self.CargoObject:GetCoordinate() -Distance=Coordinate:Get2DDistance(CargoCoordinate) -self:T(Distance) -if Distance<=self.LoadRadius then -return true -end -end -return false -end -function CARGO:IsInReportRadius(Coordinate) -self:F({Coordinate}) -local Distance=0 -if self:IsUnLoaded()then -Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate()) -self:T(Distance) -if Distance<=self.LoadRadius then -return true -end -end -return false -end -function CARGO:IsNear(Coordinate,NearRadius) -if self.CargoObject:IsAlive()then -local Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate()) -if Distance<=NearRadius then -return true -end -end -return false -end -function CARGO:IsInZone(Zone) -if self:IsLoaded()then -return Zone:IsPointVec2InZone(self.CargoCarrier:GetPointVec2()) -else -if self.CargoObject:GetSize()~=0 then -return Zone:IsPointVec2InZone(self.CargoObject:GetPointVec2()) -else -return false -end -end -return nil -end -function CARGO:GetPointVec2() -return self.CargoObject:GetPointVec2() -end -function CARGO:GetCoordinate() -return self.CargoObject:GetCoordinate() -end -function CARGO:GetWeight() -return self.Weight -end -function CARGO:SetWeight(Weight) -self.Weight=Weight -return self -end -function CARGO:GetVolume() -return self.Volume -end -function CARGO:SetVolume(Volume) -self.Volume=Volume -return self -end -function CARGO:MessageToGroup(Message,CarrierGroup,Name) -MESSAGE:New(Message,20,"Cargo "..self:GetName()):ToGroup(CarrierGroup) -end -function CARGO:Report(ReportText,Action,CarrierGroup) -if not self.Reported[CarrierGroup]or not self.Reported[CarrierGroup][Action]then -self.Reported[CarrierGroup]={} -self.Reported[CarrierGroup][Action]=true -self:MessageToGroup(ReportText,CarrierGroup) -if self.ReportFlareColor then -if not self.Reported[CarrierGroup]["Flaring"]then -self:Flare(self.ReportFlareColor) -self.Reported[CarrierGroup]["Flaring"]=true -end -end -if self.ReportSmokeColor then -if not self.Reported[CarrierGroup]["Smoking"]then -self:Smoke(self.ReportSmokeColor) -self.Reported[CarrierGroup]["Smoking"]=true -end -end -end -end -function CARGO:ReportFlare(FlareColor) -self.ReportFlareColor=FlareColor -end -function CARGO:ReportSmoke(SmokeColor) -self.ReportSmokeColor=SmokeColor -end -function CARGO:ReportReset(Action,CarrierGroup) -self.Reported[CarrierGroup][Action]=nil -end -function CARGO:ReportResetAll(CarrierGroup) -self.Reported[CarrierGroup]=nil -end -function CARGO:RespawnOnDestroyed(RespawnDestroyed) -if RespawnDestroyed then -self.onenterDestroyed=function(self) -self:Respawn() -end -else -self.onenterDestroyed=nil -end -end -end -do -CARGO_REPRESENTABLE={ -ClassName="CARGO_REPRESENTABLE" -} -function CARGO_REPRESENTABLE:New(CargoObject,Type,Name,LoadRadius,NearRadius) -local self=BASE:Inherit(self,CARGO:New(Type,Name,0,LoadRadius,NearRadius)) -self:F({Type,Name,LoadRadius,NearRadius}) -local Desc=CargoObject:GetDesc() -self:T({Desc=Desc}) -local Weight=math.random(80,120) -if Desc then -if Desc.typeName=="2B11 mortar"then -Weight=210 -else -Weight=Desc.massEmpty -end -end -self:SetWeight(Weight) -return self -end -function CARGO_REPRESENTABLE:Destroy() -self:F({CargoName=self:GetName()}) -return self -end -function CARGO_REPRESENTABLE:RouteTo(ToPointVec2,Speed) -self:F2(ToPointVec2) -local Points={} -local PointStartVec2=self.CargoObject:GetPointVec2() -Points[#Points+1]=PointStartVec2:WaypointGround(Speed) -Points[#Points+1]=ToPointVec2:WaypointGround(Speed) -local TaskRoute=self.CargoObject:TaskRoute(Points) -self.CargoObject:SetTask(TaskRoute,2) -return self -end -function CARGO_REPRESENTABLE:MessageToGroup(Message,TaskGroup,Name) -local CoordinateZone=ZONE_RADIUS:New("Zone",self:GetCoordinate():GetVec2(),500) -CoordinateZone:Scan({Object.Category.UNIT}) -for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do -local NearUnit=UNIT:Find(DCSUnit) -self:F({NearUnit=NearUnit}) -local NearUnitCoalition=NearUnit:GetCoalition() -local CargoCoalition=self:GetCoalition() -if NearUnitCoalition==CargoCoalition then -local Attributes=NearUnit:GetDesc() -self:F({Desc=Attributes}) -if NearUnit:HasAttribute("Trucks")then -MESSAGE:New(Message,20,NearUnit:GetCallsign().." reporting - Cargo "..self:GetName()):ToGroup(TaskGroup) -break -end -end -end -end -end -do -CARGO_REPORTABLE={ -ClassName="CARGO_REPORTABLE" -} -function CARGO_REPORTABLE:New(Type,Name,Weight,LoadRadius,NearRadius) -local self=BASE:Inherit(self,CARGO:New(Type,Name,Weight,LoadRadius,NearRadius)) -self:F({Type,Name,Weight,LoadRadius,NearRadius}) -return self -end -function CARGO_REPORTABLE:MessageToGroup(Message,TaskGroup,Name) -MESSAGE:New(Message,20,"Cargo "..self:GetName().." reporting"):ToGroup(TaskGroup) -end -end -do -CARGO_PACKAGE={ -ClassName="CARGO_PACKAGE" -} -function CARGO_PACKAGE:New(CargoCarrier,Type,Name,Weight,LoadRadius,NearRadius) -local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoCarrier,Type,Name,Weight,LoadRadius,NearRadius)) -self:F({Type,Name,Weight,LoadRadius,NearRadius}) -self:T(CargoCarrier) -self.CargoCarrier=CargoCarrier -return self -end -function CARGO_PACKAGE:onafterOnBoard(From,Event,To,CargoCarrier,Speed,BoardDistance,LoadDistance,Angle) -self:F() -self.CargoInAir=self.CargoCarrier:InAir() -self:T(self.CargoInAir) -if not self.CargoInAir then -local Points={} -local StartPointVec2=self.CargoCarrier:GetPointVec2() -local CargoCarrierHeading=CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -self:T({CargoCarrierHeading,CargoDeployHeading}) -local CargoDeployPointVec2=CargoCarrier:GetPointVec2():Translate(BoardDistance,CargoDeployHeading) -Points[#Points+1]=StartPointVec2:WaypointGround(Speed) -Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed) -local TaskRoute=self.CargoCarrier:TaskRoute(Points) -self.CargoCarrier:SetTask(TaskRoute,1) -end -self:Boarded(CargoCarrier,Speed,BoardDistance,LoadDistance,Angle) -end -function CARGO_PACKAGE:IsNear(CargoCarrier) -self:F() -local CargoCarrierPoint=CargoCarrier:GetCoordinate() -local Distance=CargoCarrierPoint:Get2DDistance(self.CargoCarrier:GetCoordinate()) -self:T(Distance) -if Distance<=self.NearRadius then -return true -else -return false -end -end -function CARGO_PACKAGE:onafterOnBoarded(From,Event,To,CargoCarrier,Speed,BoardDistance,LoadDistance,Angle) -self:F() -if self:IsNear(CargoCarrier)then -self:__Load(1,CargoCarrier,Speed,LoadDistance,Angle) -else -self:__Boarded(1,CargoCarrier,Speed,BoardDistance,LoadDistance,Angle) -end -end -function CARGO_PACKAGE:onafterUnBoard(From,Event,To,CargoCarrier,Speed,UnLoadDistance,UnBoardDistance,Radius,Angle) -self:F() -self.CargoInAir=self.CargoCarrier:InAir() -self:T(self.CargoInAir) -if not self.CargoInAir then -self:_Next(self.FsmP.UnLoad,UnLoadDistance,Angle) -local Points={} -local StartPointVec2=CargoCarrier:GetPointVec2() -local CargoCarrierHeading=self.CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -self:T({CargoCarrierHeading,CargoDeployHeading}) -local CargoDeployPointVec2=StartPointVec2:Translate(UnBoardDistance,CargoDeployHeading) -Points[#Points+1]=StartPointVec2:WaypointGround(Speed) -Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed) -local TaskRoute=CargoCarrier:TaskRoute(Points) -CargoCarrier:SetTask(TaskRoute,1) -end -self:__UnBoarded(1,CargoCarrier,Speed) -end -function CARGO_PACKAGE:onafterUnBoarded(From,Event,To,CargoCarrier,Speed) -self:F() -if self:IsNear(CargoCarrier)then -self:__UnLoad(1,CargoCarrier,Speed) -else -self:__UnBoarded(1,CargoCarrier,Speed) -end -end -function CARGO_PACKAGE:onafterLoad(From,Event,To,CargoCarrier,Speed,LoadDistance,Angle) -self:F() -self.CargoCarrier=CargoCarrier -local StartPointVec2=self.CargoCarrier:GetPointVec2() -local CargoCarrierHeading=self.CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -local CargoDeployPointVec2=StartPointVec2:Translate(LoadDistance,CargoDeployHeading) -local Points={} -Points[#Points+1]=StartPointVec2:WaypointGround(Speed) -Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed) -local TaskRoute=self.CargoCarrier:TaskRoute(Points) -self.CargoCarrier:SetTask(TaskRoute,1) -end -function CARGO_PACKAGE:onafterUnLoad(From,Event,To,CargoCarrier,Speed,Distance,Angle) -self:F() -local StartPointVec2=self.CargoCarrier:GetPointVec2() -local CargoCarrierHeading=self.CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -local CargoDeployPointVec2=StartPointVec2:Translate(Distance,CargoDeployHeading) -self.CargoCarrier=CargoCarrier -local Points={} -Points[#Points+1]=StartPointVec2:WaypointGround(Speed) -Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed) -local TaskRoute=self.CargoCarrier:TaskRoute(Points) -self.CargoCarrier:SetTask(TaskRoute,1) -end -end -do -CARGO_UNIT={ -ClassName="CARGO_UNIT" -} -function CARGO_UNIT:New(CargoUnit,Type,Name,LoadRadius,NearRadius) -local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoUnit,Type,Name,LoadRadius,NearRadius)) -self:T({Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) -self.CargoObject=CargoUnit -self:SetEventPriority(5) -return self -end -function CARGO_UNIT:onenterUnBoarding(From,Event,To,ToPointVec2,NearRadius) -self:F({From,Event,To,ToPointVec2,NearRadius}) -local Angle=180 -local Speed=60 -local DeployDistance=9 -local RouteDistance=60 -if From=="Loaded"then -if not self:IsDestroyed()then -local CargoCarrier=self.CargoCarrier -if CargoCarrier:IsAlive()then -local CargoCarrierPointVec2=CargoCarrier:GetPointVec2() -local CargoCarrierHeading=self.CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -local CargoRoutePointVec2=CargoCarrierPointVec2:Translate(RouteDistance,CargoDeployHeading) -local FromDirectionVec3=CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2 or CargoRoutePointVec2) -local FromAngle=CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3) -local FromPointVec2=CargoCarrierPointVec2:Translate(DeployDistance,FromAngle) -ToPointVec2=ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius(NearRadius,DeployDistance) -if self.CargoObject then -if CargoCarrier:IsShip()then -self.CargoObject:ReSpawnAt(ToPointVec2,CargoDeployHeading) -else -self.CargoObject:ReSpawnAt(FromPointVec2,CargoDeployHeading) -end -self:F({"CargoUnits:",self.CargoObject:GetGroup():GetName()}) -self.CargoCarrier=nil -local Points={} -Points[#Points+1]=FromPointVec2:WaypointGround(Speed,"Vee") -Points[#Points+1]=ToPointVec2:WaypointGround(Speed,"Vee") -local TaskRoute=self.CargoObject:TaskRoute(Points) -self.CargoObject:SetTask(TaskRoute,1) -self:__UnBoarding(1,ToPointVec2,NearRadius) -end -else -self:Destroyed() -end -end -end -end -function CARGO_UNIT:onleaveUnBoarding(From,Event,To,ToPointVec2,NearRadius) -self:F({From,Event,To,ToPointVec2,NearRadius}) -local Angle=180 -local Speed=10 -local Distance=5 -if From=="UnBoarding"then -return true -end -end -function CARGO_UNIT:onafterUnBoarding(From,Event,To,ToPointVec2,NearRadius) -self:F({From,Event,To,ToPointVec2,NearRadius}) -self.CargoInAir=self.CargoObject:InAir() -self:T(self.CargoInAir) -if not self.CargoInAir then -end -self:__UnLoad(1,ToPointVec2,NearRadius) -end -function CARGO_UNIT:onenterUnLoaded(From,Event,To,ToPointVec2) -self:F({ToPointVec2,From,Event,To}) -local Angle=180 -local Speed=10 -local Distance=5 -if From=="Loaded"then -local StartPointVec2=self.CargoCarrier:GetPointVec2() -local CargoCarrierHeading=self.CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -local CargoDeployCoord=StartPointVec2:Translate(Distance,CargoDeployHeading) -ToPointVec2=ToPointVec2 or COORDINATE:New(CargoDeployCoord.x,CargoDeployCoord.z) -if self.CargoObject then -self.CargoObject:ReSpawnAt(ToPointVec2,0) -self.CargoCarrier=nil -end -end -if self.OnUnLoadedCallBack then -self.OnUnLoadedCallBack(self,unpack(self.OnUnLoadedParameters)) -self.OnUnLoadedCallBack=nil -end -end -function CARGO_UNIT:onafterBoard(From,Event,To,CargoCarrier,NearRadius,...) -self:F({From,Event,To,CargoCarrier,NearRadius=NearRadius}) -self.CargoInAir=self.CargoObject:InAir() -local Desc=self.CargoObject:GetDesc() -local MaxSpeed=Desc.speedMaxOffRoad -local TypeName=Desc.typeName -if not self.CargoInAir then -local NearRadius=NearRadius or CargoCarrier:GetBoundingRadius()+5 -if self:IsNear(CargoCarrier:GetPointVec2(),NearRadius)then -self:Load(CargoCarrier,NearRadius,...) -else -if MaxSpeed and MaxSpeed==0 or TypeName and TypeName=="Stinger comm"then -self:Load(CargoCarrier,NearRadius,...) -else -local Speed=90 -local Angle=180 -local Distance=0 -local CargoCarrierPointVec2=CargoCarrier:GetPointVec2() -local CargoCarrierHeading=CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -local CargoDeployPointVec2=CargoCarrierPointVec2:Translate(Distance,CargoDeployHeading) -self.CargoObject:OptionAlarmStateGreen() -local Points={} -local PointStartVec2=self.CargoObject:GetPointVec2() -Points[#Points+1]=PointStartVec2:WaypointGround(Speed) -Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed) -local TaskRoute=self.CargoObject:TaskRoute(Points) -self.CargoObject:SetTask(TaskRoute,2) -self:__Boarding(-5,CargoCarrier,NearRadius,...) -self.RunCount=0 -end -end -end -end -function CARGO_UNIT:onafterBoarding(From,Event,To,CargoCarrier,NearRadius,...) -self:F({From,Event,To,CargoCarrier:GetName(),NearRadius=NearRadius}) -self:F({IsAlive=self.CargoObject:IsAlive()}) -if CargoCarrier and CargoCarrier:IsAlive()then -if(CargoCarrier:IsAir()and not CargoCarrier:InAir())or true then -local NearRadius=NearRadius or CargoCarrier:GetBoundingRadius(NearRadius)+5 -if self:IsNear(CargoCarrier:GetPointVec2(),NearRadius)then -self:__Load(-1,CargoCarrier,...) -else -if self:IsNear(CargoCarrier:GetPointVec2(),20)then -self:__Boarding(-1,CargoCarrier,NearRadius,...) -self.RunCount=self.RunCount+1 -else -self:__Boarding(-2,CargoCarrier,NearRadius,...) -self.RunCount=self.RunCount+2 -end -if self.RunCount>=40 then -self.RunCount=0 -local Speed=90 -local Angle=180 -local Distance=0 -local CargoCarrierPointVec2=CargoCarrier:GetPointVec2() -local CargoCarrierHeading=CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -local CargoDeployPointVec2=CargoCarrierPointVec2:Translate(Distance,CargoDeployHeading) -self.CargoObject:OptionAlarmStateGreen() -local Points={} -local PointStartVec2=self.CargoObject:GetPointVec2() -Points[#Points+1]=PointStartVec2:WaypointGround(Speed,"Off road") -Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed,"Off road") -local TaskRoute=self.CargoObject:TaskRoute(Points) -self.CargoObject:SetTask(TaskRoute,0.2) -end -end -else -self.CargoObject:MessageToGroup("Cancelling Boarding... Get back on the ground!",5,CargoCarrier:GetGroup(),self:GetName()) -self:CancelBoarding(CargoCarrier,NearRadius,...) -self.CargoObject:SetCommand(self.CargoObject:CommandStopRoute(true)) -end -else -self:E("Something is wrong") -end -end -function CARGO_UNIT:onenterLoaded(From,Event,To,CargoCarrier) -self:F({From,Event,To,CargoCarrier}) -self.CargoCarrier=CargoCarrier -if self.CargoObject then -self.CargoObject:Destroy(false) -end -end -function CARGO_UNIT:GetTransportationMethod() -if self:IsLoaded()then -return"for unboarding" -else -if self:IsUnLoaded()then -return"for boarding" -else -if self:IsDeployed()then -return"delivered" -end -end -end -return"" -end -end -do -CARGO_SLINGLOAD={ -ClassName="CARGO_SLINGLOAD" -} -function CARGO_SLINGLOAD:New(CargoStatic,Type,Name,LoadRadius,NearRadius) -local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoStatic,Type,Name,nil,LoadRadius,NearRadius)) -self:F({Type,Name,NearRadius}) -self.CargoObject=CargoStatic -_EVENTDISPATCHER:CreateEventNewCargo(self) -self:HandleEvent(EVENTS.Dead,self.OnEventCargoDead) -self:HandleEvent(EVENTS.Crash,self.OnEventCargoDead) -self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventCargoDead) -self:SetEventPriority(4) -self.NearRadius=NearRadius or 25 -return self -end -function CARGO_SLINGLOAD:OnEventCargoDead(EventData) -local Destroyed=false -if self:IsDestroyed()or self:IsUnLoaded()then -if self.CargoObject:GetName()==EventData.IniUnitName then -if not self.NoDestroy then -Destroyed=true -end -end -end -if Destroyed then -self:I({"Cargo crate destroyed: "..self.CargoObject:GetName()}) -self:Destroyed() -end -end -function CARGO_SLINGLOAD:CanSlingload() -return true -end -function CARGO_SLINGLOAD:CanBoard() -return false -end -function CARGO_SLINGLOAD:CanUnboard() -return false -end -function CARGO_SLINGLOAD:CanLoad() -return false -end -function CARGO_SLINGLOAD:CanUnload() -return false -end -function CARGO_SLINGLOAD:IsInReportRadius(Coordinate) -local Distance=0 -if self:IsUnLoaded()then -Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate()) -if Distance<=self.LoadRadius then -return true -end -end -return false -end -function CARGO_SLINGLOAD:IsInLoadRadius(Coordinate) -local Distance=0 -if self:IsUnLoaded()then -Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate()) -if Distance<=self.NearRadius then -return true -end -end -return false -end -function CARGO_SLINGLOAD:GetCoordinate() -return self.CargoObject:GetCoordinate() -end -function CARGO_SLINGLOAD:IsAlive() -local Alive=true -if self:IsLoaded()then -Alive=Alive==true and self.CargoCarrier:IsAlive() -else -Alive=Alive==true and self.CargoObject:IsAlive() -end -return Alive -end -function CARGO_SLINGLOAD:RouteTo(Coordinate) -end -function CARGO_SLINGLOAD:IsNear(CargoCarrier,NearRadius) -return self:IsNear(CargoCarrier:GetCoordinate(),NearRadius) -end -function CARGO_SLINGLOAD:Respawn() -if self.CargoObject then -self.CargoObject:ReSpawn() -self:__Reset(-0.1) -end -end -function CARGO_SLINGLOAD:onafterReset() -if self.CargoObject then -self:SetDeployed(false) -self:SetStartState("UnLoaded") -self.CargoCarrier=nil -_EVENTDISPATCHER:CreateEventNewCargo(self) -end -end -function CARGO_SLINGLOAD:GetTransportationMethod() -if self:IsLoaded()then -return"for sling loading" -else -if self:IsUnLoaded()then -return"for sling loading" -else -if self:IsDeployed()then -return"delivered" -end -end -end -return"" -end -end -do -CARGO_CRATE={ -ClassName="CARGO_CRATE" -} -function CARGO_CRATE:New(CargoStatic,Type,Name,LoadRadius,NearRadius) -local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoStatic,Type,Name,nil,LoadRadius,NearRadius)) -self:F({Type,Name,NearRadius}) -self.CargoObject=CargoStatic -_EVENTDISPATCHER:CreateEventNewCargo(self) -self:HandleEvent(EVENTS.Dead,self.OnEventCargoDead) -self:HandleEvent(EVENTS.Crash,self.OnEventCargoDead) -self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventCargoDead) -self:SetEventPriority(4) -self.NearRadius=NearRadius or 25 -return self -end -function CARGO_CRATE:OnEventCargoDead(EventData) -local Destroyed=false -if self:IsDestroyed()or self:IsUnLoaded()or self:IsBoarding()then -if self.CargoObject:GetName()==EventData.IniUnitName then -if not self.NoDestroy then -Destroyed=true -end -end -else -if self:IsLoaded()then -local CarrierName=self.CargoCarrier:GetName() -if CarrierName==EventData.IniDCSUnitName then -MESSAGE:New("Cargo is lost from carrier "..CarrierName,15):ToAll() -Destroyed=true -self.CargoCarrier:ClearCargo() -end -end -end -if Destroyed then -self:I({"Cargo crate destroyed: "..self.CargoObject:GetName()}) -self:Destroyed() -end -end -function CARGO_CRATE:onenterUnLoaded(From,Event,To,ToPointVec2) -local Angle=180 -local Speed=10 -local Distance=10 -if From=="Loaded"then -local StartCoordinate=self.CargoCarrier:GetCoordinate() -local CargoCarrierHeading=self.CargoCarrier:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -local CargoDeployCoord=StartCoordinate:Translate(Distance,CargoDeployHeading) -ToPointVec2=ToPointVec2 or COORDINATE:NewFromVec2({x=CargoDeployCoord.x,y=CargoDeployCoord.z}) -if self.CargoObject then -self.CargoObject:ReSpawnAt(ToPointVec2,0) -self.CargoCarrier=nil -end -end -if self.OnUnLoadedCallBack then -self.OnUnLoadedCallBack(self,unpack(self.OnUnLoadedParameters)) -self.OnUnLoadedCallBack=nil -end -end -function CARGO_CRATE:onenterLoaded(From,Event,To,CargoCarrier) -self.CargoCarrier=CargoCarrier -if self.CargoObject then -self:T("Destroying") -self.NoDestroy=true -self.CargoObject:Destroy(false) -end -end -function CARGO_CRATE:CanBoard() -return false -end -function CARGO_CRATE:CanUnboard() -return false -end -function CARGO_CRATE:CanSlingload() -return false -end -function CARGO_CRATE:IsInReportRadius(Coordinate) -local Distance=0 -if self:IsUnLoaded()then -Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate()) -if Distance<=self.LoadRadius then -return true -end -end -return false -end -function CARGO_CRATE:IsInLoadRadius(Coordinate) -local Distance=0 -if self:IsUnLoaded()then -Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate()) -if Distance<=self.NearRadius then -return true -end -end -return false -end -function CARGO_CRATE:GetCoordinate() -return self.CargoObject:GetCoordinate() -end -function CARGO_CRATE:IsAlive() -local Alive=true -if self:IsLoaded()then -Alive=Alive==true and self.CargoCarrier:IsAlive() -else -Alive=Alive==true and self.CargoObject:IsAlive() -end -return Alive -end -function CARGO_CRATE:RouteTo(Coordinate) -self:F({Coordinate=Coordinate}) -end -function CARGO_CRATE:IsNear(CargoCarrier,NearRadius) -self:F({NearRadius=NearRadius}) -return self:IsNear(CargoCarrier:GetCoordinate(),NearRadius) -end -function CARGO_CRATE:Respawn() -self:F({"Respawning crate "..self:GetName()}) -if self.CargoObject then -self.CargoObject:ReSpawn() -self:__Reset(-0.1) -end -end -function CARGO_CRATE:onafterReset() -self:F({"Reset crate "..self:GetName()}) -if self.CargoObject then -self:SetDeployed(false) -self:SetStartState("UnLoaded") -self.CargoCarrier=nil -_EVENTDISPATCHER:CreateEventNewCargo(self) -end -end -function CARGO_CRATE:GetTransportationMethod() -if self:IsLoaded()then -return"for unloading" -else -if self:IsUnLoaded()then -return"for loading" -else -if self:IsDeployed()then -return"delivered" -end -end -end -return"" -end -end -do -CARGO_GROUP={ -ClassName="CARGO_GROUP", -} -function CARGO_GROUP:New(CargoGroup,Type,Name,LoadRadius,NearRadius) -local self=BASE:Inherit(self,CARGO_REPORTABLE:New(Type,Name,0,LoadRadius,NearRadius)) -self:F({Type,Name,LoadRadius}) -self.CargoSet=SET_CARGO:New() -self.CargoGroup=CargoGroup -self.Grouped=true -self.CargoUnitTemplate={} -self.NearRadius=NearRadius -self:SetDeployed(false) -local WeightGroup=0 -local VolumeGroup=0 -self.CargoGroup:Destroy() -local GroupName=CargoGroup:GetName() -self.CargoName=Name -self.CargoTemplate=UTILS.DeepCopy(_DATABASE:GetGroupTemplate(GroupName)) -self.CargoTemplate.lateActivation=false -self.GroupTemplate=UTILS.DeepCopy(self.CargoTemplate) -self.GroupTemplate.name=self.CargoName.."#CARGO" -self.GroupTemplate.groupId=nil -self.GroupTemplate.units={} -for UnitID,UnitTemplate in pairs(self.CargoTemplate.units)do -UnitTemplate.name=UnitTemplate.name.."#CARGO" -local CargoUnitName=UnitTemplate.name -self.CargoUnitTemplate[CargoUnitName]=UnitTemplate -self.GroupTemplate.units[#self.GroupTemplate.units+1]=self.CargoUnitTemplate[CargoUnitName] -self.GroupTemplate.units[#self.GroupTemplate.units].unitId=nil -local Unit=UNIT:Register(CargoUnitName) -end -self.CargoGroup=GROUP:NewTemplate(self.GroupTemplate,self.GroupTemplate.CoalitionID,self.GroupTemplate.CategoryID,self.GroupTemplate.CountryID) -self.CargoObject=_DATABASE:Spawn(self.GroupTemplate) -for CargoUnitID,CargoUnit in pairs(self.CargoObject:GetUnits())do -local CargoUnitName=CargoUnit:GetName() -local Cargo=CARGO_UNIT:New(CargoUnit,Type,CargoUnitName,LoadRadius,NearRadius) -self.CargoSet:Add(CargoUnitName,Cargo) -WeightGroup=WeightGroup+Cargo:GetWeight() -end -self:SetWeight(WeightGroup) -self:T({"Weight Cargo",WeightGroup}) -_EVENTDISPATCHER:CreateEventNewCargo(self) -self:HandleEvent(EVENTS.Dead,self.OnEventCargoDead) -self:HandleEvent(EVENTS.Crash,self.OnEventCargoDead) -self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventCargoDead) -self:SetEventPriority(4) -return self -end -function CARGO_GROUP:Respawn() -self:F({"Respawning"}) -for CargoID,CargoData in pairs(self.CargoSet:GetSet())do -local Cargo=CargoData -Cargo:Destroy() -Cargo:SetStartState("UnLoaded") -end -_DATABASE:Spawn(self.GroupTemplate) -for CargoUnitID,CargoUnit in pairs(self.CargoObject:GetUnits())do -local CargoUnitName=CargoUnit:GetName() -local Cargo=CARGO_UNIT:New(CargoUnit,self.Type,CargoUnitName,self.LoadRadius) -self.CargoSet:Add(CargoUnitName,Cargo) -end -self:SetDeployed(false) -self:SetStartState("UnLoaded") -end -function CARGO_GROUP:Ungroup() -if self.Grouped==true then -self.Grouped=false -self.CargoGroup:Destroy() -for CargoUnitName,CargoUnit in pairs(self.CargoSet:GetSet())do -local CargoUnit=CargoUnit -if CargoUnit:IsUnLoaded()then -local GroupTemplate=UTILS.DeepCopy(self.CargoTemplate) -GroupTemplate.name=self.CargoName.."#CARGO#"..CargoUnitName -GroupTemplate.groupId=nil -if CargoUnit:IsUnLoaded()then -GroupTemplate.units={} -GroupTemplate.units[1]=self.CargoUnitTemplate[CargoUnitName] -GroupTemplate.units[#GroupTemplate.units].unitId=nil -GroupTemplate.units[#GroupTemplate.units].x=CargoUnit:GetX() -GroupTemplate.units[#GroupTemplate.units].y=CargoUnit:GetY() -GroupTemplate.units[#GroupTemplate.units].heading=CargoUnit:GetHeading() -end -local CargoGroup=GROUP:NewTemplate(GroupTemplate,GroupTemplate.CoalitionID,GroupTemplate.CategoryID,GroupTemplate.CountryID) -_DATABASE:Spawn(GroupTemplate) -end -end -self.CargoObject=nil -end -end -function CARGO_GROUP:Regroup() -self:F("Regroup") -if self.Grouped==false then -self.Grouped=true -local GroupTemplate=UTILS.DeepCopy(self.CargoTemplate) -GroupTemplate.name=self.CargoName.."#CARGO" -GroupTemplate.groupId=nil -GroupTemplate.units={} -for CargoUnitName,CargoUnit in pairs(self.CargoSet:GetSet())do -local CargoUnit=CargoUnit -self:F({CargoUnit:GetName(),UnLoaded=CargoUnit:IsUnLoaded()}) -if CargoUnit:IsUnLoaded()then -CargoUnit.CargoObject:Destroy() -GroupTemplate.units[#GroupTemplate.units+1]=self.CargoUnitTemplate[CargoUnitName] -GroupTemplate.units[#GroupTemplate.units].unitId=nil -GroupTemplate.units[#GroupTemplate.units].x=CargoUnit:GetX() -GroupTemplate.units[#GroupTemplate.units].y=CargoUnit:GetY() -GroupTemplate.units[#GroupTemplate.units].heading=CargoUnit:GetHeading() -end -end -self.CargoGroup=GROUP:NewTemplate(GroupTemplate,GroupTemplate.CoalitionID,GroupTemplate.CategoryID,GroupTemplate.CountryID) -self:F({"Regroup",GroupTemplate}) -self.CargoObject=_DATABASE:Spawn(GroupTemplate) -end -end -function CARGO_GROUP:OnEventCargoDead(EventData) -self:E(EventData) -local Destroyed=false -if self:IsDestroyed()or self:IsUnLoaded()or self:IsBoarding()or self:IsUnboarding()then -Destroyed=true -for CargoID,CargoData in pairs(self.CargoSet:GetSet())do -local Cargo=CargoData -if Cargo:IsAlive()then -Destroyed=false -else -Cargo:Destroyed() -end -end -else -local CarrierName=self.CargoCarrier:GetName() -if CarrierName==EventData.IniDCSUnitName then -MESSAGE:New("Cargo is lost from carrier "..CarrierName,15):ToAll() -Destroyed=true -self.CargoCarrier:ClearCargo() -end -end -if Destroyed then -self:Destroyed() -self:E({"Cargo group destroyed"}) -end -end -function CARGO_GROUP:onafterBoard(From,Event,To,CargoCarrier,NearRadius,...) -self:F({CargoCarrier.UnitName,From,Event,To,NearRadius=NearRadius}) -NearRadius=NearRadius or self.NearRadius -self.CargoSet:ForEach( -function(Cargo,...) -self:F({"Board Unit",Cargo:GetName(),Cargo:IsDestroyed(),Cargo.CargoObject:IsAlive()}) -local CargoGroup=Cargo.CargoObject -CargoGroup:OptionAlarmStateGreen() -Cargo:__Board(1,CargoCarrier,NearRadius,...) -end,... -) -self:__Boarding(-1,CargoCarrier,NearRadius,...) -end -function CARGO_GROUP:onafterLoad(From,Event,To,CargoCarrier,...) -if From=="UnLoaded"then -for CargoID,Cargo in pairs(self.CargoSet:GetSet())do -if not Cargo:IsDestroyed()then -Cargo:Load(CargoCarrier) -end -end -end -self.CargoCarrier=CargoCarrier -self.CargoCarrier:AddCargo(self) -end -function CARGO_GROUP:onafterBoarding(From,Event,To,CargoCarrier,NearRadius,...) -local Boarded=true -local Cancelled=false -local Dead=true -self.CargoSet:Flush() -for CargoID,Cargo in pairs(self.CargoSet:GetSet())do -if not Cargo:is("Loaded") -and(not Cargo:is("Destroyed"))then -Boarded=false -end -if Cargo:is("UnLoaded")then -Cancelled=true -end -if not Cargo:is("Destroyed")then -Dead=false -end -end -if not Dead then -if not Cancelled then -if not Boarded then -self:__Boarding(-5,CargoCarrier,NearRadius,...) -else -self:F("Group Cargo is loaded") -self:__Load(1,CargoCarrier,...) -end -else -self:__CancelBoarding(1,CargoCarrier,NearRadius,...) -end -else -self:__Destroyed(1,CargoCarrier,NearRadius,...) -end -end -function CARGO_GROUP:onafterUnBoard(From,Event,To,ToPointVec2,NearRadius,...) -self:F({From,Event,To,ToPointVec2,NearRadius}) -NearRadius=NearRadius or 25 -local Timer=1 -if From=="Loaded"then -if self.CargoObject then -self.CargoObject:Destroy() -end -self.CargoSet:ForEach( -function(Cargo,NearRadius) -if not Cargo:IsDestroyed()then -local ToVec=nil -if ToPointVec2==nil then -ToVec=self.CargoCarrier:GetPointVec2():GetRandomPointVec2InRadius(2*NearRadius,NearRadius) -else -ToVec=ToPointVec2 -end -Cargo:__UnBoard(Timer,ToVec,NearRadius) -Timer=Timer+1 -end -end,{NearRadius} -) -self:__UnBoarding(1,ToPointVec2,NearRadius,...) -end -end -function CARGO_GROUP:onafterUnBoarding(From,Event,To,ToPointVec2,NearRadius,...) -local Angle=180 -local Speed=10 -local Distance=5 -if From=="UnBoarding"then -local UnBoarded=true -for CargoID,Cargo in pairs(self.CargoSet:GetSet())do -self:T({Cargo:GetName(),Cargo.current}) -if not Cargo:is("UnLoaded")and not Cargo:IsDestroyed()then -UnBoarded=false -end -end -if UnBoarded then -self:__UnLoad(1,ToPointVec2,...) -else -self:__UnBoarding(1,ToPointVec2,NearRadius,...) -end -return false -end -end -function CARGO_GROUP:onafterUnLoad(From,Event,To,ToPointVec2,...) -if From=="Loaded"then -self.CargoSet:ForEach( -function(Cargo) -local RandomVec2=nil -if ToPointVec2 then -RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20,10) -end -Cargo:UnBoard(RandomVec2) -end -) -end -self.CargoCarrier:RemoveCargo(self) -self.CargoCarrier=nil -end -function CARGO_GROUP:GetCoordinate() -local Cargo=self:GetFirstAlive() -if Cargo then -return Cargo.CargoObject:GetCoordinate() -end -return nil -end -function CARGO:GetX() -local Cargo=self:GetFirstAlive() -if Cargo then -return Cargo:GetCoordinate().x -end -return nil -end -function CARGO:GetY() -local Cargo=self:GetFirstAlive() -if Cargo then -return Cargo:GetCoordinate().z -end -return nil -end -function CARGO_GROUP:IsAlive() -local Cargo=self:GetFirstAlive() -return Cargo~=nil -end -function CARGO_GROUP:GetFirstAlive() -local CargoFirstAlive=nil -for _,Cargo in pairs(self.CargoSet:GetSet())do -if not Cargo:IsDestroyed()then -CargoFirstAlive=Cargo -break -end -end -return CargoFirstAlive -end -function CARGO_GROUP:GetCount() -return self.CargoSet:Count() -end -function CARGO_GROUP:GetGroup(Cargo) -local Cargo=Cargo or self:GetFirstAlive() -return Cargo.CargoObject:GetGroup() -end -function CARGO_GROUP:RouteTo(Coordinate) -self.CargoSet:ForEach( -function(Cargo) -Cargo.CargoObject:RouteGroundTo(Coordinate,10,"vee",0) -end -) -end -function CARGO_GROUP:IsNear(CargoCarrier,NearRadius) -self:F({NearRadius=NearRadius}) -for _,Cargo in pairs(self.CargoSet:GetSet())do -local Cargo=Cargo -if Cargo:IsAlive()then -if Cargo:IsNear(CargoCarrier:GetCoordinate(),NearRadius)then -self:F("Near") -return true -end -end -end -return nil -end -function CARGO_GROUP:IsInLoadRadius(Coordinate) -local Cargo=self:GetFirstAlive() -if Cargo then -local Distance=0 -local CargoCoordinate -if Cargo:IsLoaded()then -CargoCoordinate=Cargo.CargoCarrier:GetCoordinate() -else -CargoCoordinate=Cargo.CargoObject:GetCoordinate() -end -if CargoCoordinate then -Distance=Coordinate:Get2DDistance(CargoCoordinate) -else -return false -end -self:F({Distance=Distance,LoadRadius=self.LoadRadius}) -if Distance<=self.LoadRadius then -return true -else -return false -end -end -return nil -end -function CARGO_GROUP:IsInReportRadius(Coordinate) -local Cargo=self:GetFirstAlive() -if Cargo then -self:F({Cargo}) -local Distance=0 -if Cargo:IsUnLoaded()then -Distance=Coordinate:Get2DDistance(Cargo.CargoObject:GetCoordinate()) -if Distance<=self.LoadRadius then -return true -end -end -end -return nil -end -function CARGO_GROUP:Flare(FlareColor) -local Cargo=self.CargoSet:GetFirst() -if Cargo then -Cargo:Flare(FlareColor) -end -end -function CARGO_GROUP:Smoke(SmokeColor,Radius) -local Cargo=self.CargoSet:GetFirst() -if Cargo then -Cargo:Smoke(SmokeColor,Radius) -end -end -function CARGO_GROUP:IsInZone(Zone) -local Cargo=self.CargoSet:GetFirst() -if Cargo then -return Cargo:IsInZone(Zone) -end -return nil -end -function CARGO_GROUP:GetTransportationMethod() -if self:IsLoaded()then -return"for unboarding" -else -if self:IsUnLoaded()then -return"for boarding" -else -if self:IsDeployed()then -return"delivered" -end -end -end -return"" -end -end -SCORING={ -ClassName="SCORING", -ClassID=0, -Players={}, -} -local _SCORINGCoalition= -{ -[1]="Red", -[2]="Blue", -} -local _SCORINGCategory= -{ -[Unit.Category.AIRPLANE]="Plane", -[Unit.Category.HELICOPTER]="Helicopter", -[Unit.Category.GROUND_UNIT]="Vehicle", -[Unit.Category.SHIP]="Ship", -[Unit.Category.STRUCTURE]="Structure", -} -function SCORING:New(GameName) -local self=BASE:Inherit(self,BASE:New()) -if GameName then -self.GameName=GameName -else -error("A game name must be given to register the scoring results") -end -self.ScoringObjects={} -self.ScoringZones={} -self:SetMessagesToAll() -self:SetMessagesHit(false) -self:SetMessagesDestroy(true) -self:SetMessagesScore(true) -self:SetMessagesZone(true) -self:SetScaleDestroyScore(10) -self:SetScaleDestroyPenalty(30) -self:SetFratricide(self.ScaleDestroyPenalty*3) -self:SetCoalitionChangePenalty(self.ScaleDestroyPenalty) -self:SetDisplayMessagePrefix() -self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash) -self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash) -self:HandleEvent(EVENTS.Hit,self._EventOnHit) -self:HandleEvent(EVENTS.Birth) -self:HandleEvent(EVENTS.PlayerLeaveUnit) -self.ScoringPlayerScan=BASE:ScheduleOnce(1, -function() -for PlayerName,PlayerUnit in pairs(_DATABASE:GetPlayerUnits())do -self:_AddPlayerFromUnit(PlayerUnit) -self:SetScoringMenu(PlayerUnit:GetGroup()) -end -end -) -self:OpenCSV(GameName) -return self -end -function SCORING:SetDisplayMessagePrefix(DisplayMessagePrefix) -self.DisplayMessagePrefix=DisplayMessagePrefix or"" -return self -end -function SCORING:SetScaleDestroyScore(Scale) -self.ScaleDestroyScore=Scale -return self -end -function SCORING:SetScaleDestroyPenalty(Scale) -self.ScaleDestroyPenalty=Scale -return self -end -function SCORING:AddUnitScore(ScoreUnit,Score) -local UnitName=ScoreUnit:GetName() -self.ScoringObjects[UnitName]=Score -return self -end -function SCORING:RemoveUnitScore(ScoreUnit) -local UnitName=ScoreUnit:GetName() -self.ScoringObjects[UnitName]=nil -return self -end -function SCORING:AddStaticScore(ScoreStatic,Score) -local StaticName=ScoreStatic:GetName() -self.ScoringObjects[StaticName]=Score -return self -end -function SCORING:RemoveStaticScore(ScoreStatic) -local StaticName=ScoreStatic:GetName() -self.ScoringObjects[StaticName]=nil -return self -end -function SCORING:AddScoreGroup(ScoreGroup,Score) -local ScoreUnits=ScoreGroup:GetUnits() -for ScoreUnitID,ScoreUnit in pairs(ScoreUnits)do -local UnitName=ScoreUnit:GetName() -self.ScoringObjects[UnitName]=Score -end -return self -end -function SCORING:AddZoneScore(ScoreZone,Score) -local ZoneName=ScoreZone:GetName() -self.ScoringZones[ZoneName]={} -self.ScoringZones[ZoneName].ScoreZone=ScoreZone -self.ScoringZones[ZoneName].Score=Score -return self -end -function SCORING:RemoveZoneScore(ScoreZone) -local ZoneName=ScoreZone:GetName() -self.ScoringZones[ZoneName]=nil -return self -end -function SCORING:SetMessagesHit(OnOff) -self.MessagesHit=OnOff -return self -end -function SCORING:IfMessagesHit() -return self.MessagesHit -end -function SCORING:SetMessagesDestroy(OnOff) -self.MessagesDestroy=OnOff -return self -end -function SCORING:IfMessagesDestroy() -return self.MessagesDestroy -end -function SCORING:SetMessagesScore(OnOff) -self.MessagesScore=OnOff -return self -end -function SCORING:IfMessagesScore() -return self.MessagesScore -end -function SCORING:SetMessagesZone(OnOff) -self.MessagesZone=OnOff -return self -end -function SCORING:IfMessagesZone() -return self.MessagesZone -end -function SCORING:SetMessagesToAll() -self.MessagesAudience=1 -return self -end -function SCORING:IfMessagesToAll() -return self.MessagesAudience==1 -end -function SCORING:SetMessagesToCoalition() -self.MessagesAudience=2 -return self -end -function SCORING:IfMessagesToCoalition() -return self.MessagesAudience==2 -end -function SCORING:SetFratricide(Fratricide) -self.Fratricide=Fratricide -return self -end -function SCORING:SetCoalitionChangePenalty(CoalitionChangePenalty) -self.CoalitionChangePenalty=CoalitionChangePenalty -return self -end -function SCORING:SetScoringMenu(ScoringGroup) -local Menu=MENU_GROUP:New(ScoringGroup,'Scoring and Statistics') -local ReportGroupSummary=MENU_GROUP_COMMAND:New(ScoringGroup,'Summary report players in group',Menu,SCORING.ReportScoreGroupSummary,self,ScoringGroup) -local ReportGroupDetailed=MENU_GROUP_COMMAND:New(ScoringGroup,'Detailed report players in group',Menu,SCORING.ReportScoreGroupDetailed,self,ScoringGroup) -local ReportToAllSummary=MENU_GROUP_COMMAND:New(ScoringGroup,'Summary report all players',Menu,SCORING.ReportScoreAllSummary,self,ScoringGroup) -self:SetState(ScoringGroup,"ScoringMenu",Menu) -return self -end -function SCORING:_AddPlayerFromUnit(UnitData) -self:F(UnitData) -if UnitData:IsAlive()then -local UnitName=UnitData:GetName() -local PlayerName=UnitData:GetPlayerName() -local UnitDesc=UnitData:GetDesc() -local UnitCategory=UnitDesc.category -local UnitCoalition=UnitData:GetCoalition() -local UnitTypeName=UnitData:GetTypeName() -local UnitThreatLevel,UnitThreatType=UnitData:GetThreatLevel() -self:T({PlayerName,UnitName,UnitCategory,UnitCoalition,UnitTypeName}) -if self.Players[PlayerName]==nil then -self.Players[PlayerName]={} -self.Players[PlayerName].Hit={} -self.Players[PlayerName].Destroy={} -self.Players[PlayerName].Goals={} -self.Players[PlayerName].Mission={} -self.Players[PlayerName].HitPlayers={} -self.Players[PlayerName].Score=0 -self.Players[PlayerName].Penalty=0 -self.Players[PlayerName].PenaltyCoalition=0 -self.Players[PlayerName].PenaltyWarning=0 -end -if not self.Players[PlayerName].UnitCoalition then -self.Players[PlayerName].UnitCoalition=UnitCoalition -else -if self.Players[PlayerName].UnitCoalition~=UnitCoalition then -self.Players[PlayerName].Penalty=self.Players[PlayerName].Penalty+50 -self.Players[PlayerName].PenaltyCoalition=self.Players[PlayerName].PenaltyCoalition+1 -MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' changed coalition from ".._SCORINGCoalition[self.Players[PlayerName].UnitCoalition].." to ".._SCORINGCoalition[UnitCoalition].. -"(changed "..self.Players[PlayerName].PenaltyCoalition.." times the coalition). 50 Penalty points added.", -MESSAGE.Type.Information -):ToAll() -self:ScoreCSV(PlayerName,"","COALITION_PENALTY",1,-50,self.Players[PlayerName].UnitName,_SCORINGCoalition[self.Players[PlayerName].UnitCoalition],_SCORINGCategory[self.Players[PlayerName].UnitCategory],self.Players[PlayerName].UnitType, -UnitName,_SCORINGCoalition[UnitCoalition],_SCORINGCategory[UnitCategory],UnitData:GetTypeName()) -end -end -self.Players[PlayerName].UnitName=UnitName -self.Players[PlayerName].UnitCoalition=UnitCoalition -self.Players[PlayerName].UnitCategory=UnitCategory -self.Players[PlayerName].UnitType=UnitTypeName -self.Players[PlayerName].UNIT=UnitData -self.Players[PlayerName].ThreatLevel=UnitThreatLevel -self.Players[PlayerName].ThreatType=UnitThreatType -end -end -function SCORING:AddGoalScorePlayer(PlayerName,GoalTag,Text,Score) -self:F({PlayerName,PlayerName,GoalTag,Text,Score}) -if PlayerName then -local PlayerData=self.Players[PlayerName] -PlayerData.Goals[GoalTag]=PlayerData.Goals[GoalTag]or{Score=0} -PlayerData.Goals[GoalTag].Score=PlayerData.Goals[GoalTag].Score+Score -PlayerData.Score=PlayerData.Score+Score -MESSAGE:NewType(self.DisplayMessagePrefix..Text,MESSAGE.Type.Information):ToAll() -self:ScoreCSV(PlayerName,"","GOAL_"..string.upper(GoalTag),1,Score,nil) -end -end -function SCORING:AddGoalScore(PlayerUnit,GoalTag,Text,Score) -local PlayerName=PlayerUnit:GetPlayerName() -self:F({PlayerUnit.UnitName,PlayerName,GoalTag,Text,Score}) -if PlayerName then -local PlayerData=self.Players[PlayerName] -PlayerData.Goals[GoalTag]=PlayerData.Goals[GoalTag]or{Score=0} -PlayerData.Goals[GoalTag].Score=PlayerData.Goals[GoalTag].Score+Score -PlayerData.Score=PlayerData.Score+Score -MESSAGE:NewType(self.DisplayMessagePrefix..Text,MESSAGE.Type.Information):ToAll() -self:ScoreCSV(PlayerName,"","GOAL_"..string.upper(GoalTag),1,Score,PlayerUnit:GetName()) -end -end -function SCORING:_AddMissionTaskScore(Mission,PlayerUnit,Text,Score) -local PlayerName=PlayerUnit:GetPlayerName() -local MissionName=Mission:GetName() -self:F({Mission:GetName(),PlayerUnit.UnitName,PlayerName,Text,Score}) -if PlayerName then -local PlayerData=self.Players[PlayerName] -if not PlayerData.Mission[MissionName]then -PlayerData.Mission[MissionName]={} -PlayerData.Mission[MissionName].ScoreTask=0 -PlayerData.Mission[MissionName].ScoreMission=0 -end -self:T(PlayerName) -self:T(PlayerData.Mission[MissionName]) -PlayerData.Score=self.Players[PlayerName].Score+Score -PlayerData.Mission[MissionName].ScoreTask=self.Players[PlayerName].Mission[MissionName].ScoreTask+Score -MESSAGE:NewType(self.DisplayMessagePrefix..Mission:GetText().." : "..Text.." Score: "..Score,MESSAGE.Type.Information):ToAll() -self:ScoreCSV(PlayerName,"","TASK_"..MissionName:gsub(' ','_'),1,Score,PlayerUnit:GetName()) -end -end -function SCORING:_AddMissionGoalScore(Mission,PlayerName,Text,Score) -local MissionName=Mission:GetName() -self:F({Mission:GetName(),PlayerName,Text,Score}) -if PlayerName then -local PlayerData=self.Players[PlayerName] -if not PlayerData.Mission[MissionName]then -PlayerData.Mission[MissionName]={} -PlayerData.Mission[MissionName].ScoreTask=0 -PlayerData.Mission[MissionName].ScoreMission=0 -end -self:T(PlayerName) -self:T(PlayerData.Mission[MissionName]) -PlayerData.Score=self.Players[PlayerName].Score+Score -PlayerData.Mission[MissionName].ScoreTask=self.Players[PlayerName].Mission[MissionName].ScoreTask+Score -MESSAGE:NewType(string.format("%s%s: %s! Player %s receives %d score!",self.DisplayMessagePrefix,Mission:GetText(),Text,PlayerName,Score),MESSAGE.Type.Information):ToAll() -self:ScoreCSV(PlayerName,"","TASK_"..MissionName:gsub(' ','_'),1,Score) -end -end -function SCORING:_AddMissionScore(Mission,Text,Score) -local MissionName=Mission:GetName() -self:F({Mission,Text,Score}) -self:F(self.Players) -for PlayerName,PlayerData in pairs(self.Players)do -self:F(PlayerData) -if PlayerData.Mission[MissionName]then -PlayerData.Score=PlayerData.Score+Score -PlayerData.Mission[MissionName].ScoreMission=PlayerData.Mission[MissionName].ScoreMission+Score -MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' has "..Text.." in "..Mission:GetText()..". ".. -Score.." mission score!", -MESSAGE.Type.Information):ToAll() -self:ScoreCSV(PlayerName,"","MISSION_"..MissionName:gsub(' ','_'),1,Score) -end -end -end -function SCORING:OnEventBirth(Event) -if Event.IniUnit then -if Event.IniObjectCategory==1 then -local PlayerName=Event.IniUnit:GetPlayerName() -if PlayerName then -self:_AddPlayerFromUnit(Event.IniUnit) -self:SetScoringMenu(Event.IniGroup) -end -end -end -end -function SCORING:OnEventPlayerLeaveUnit(Event) -if Event.IniUnit then -local Menu=self:GetState(Event.IniUnit:GetGroup(),"ScoringMenu") -if Menu then -end -end -end -function SCORING:_EventOnHit(Event) -self:F({Event}) -local InitUnit=nil -local InitUNIT=nil -local InitUnitName="" -local InitGroup=nil -local InitGroupName="" -local InitPlayerName=nil -local InitCoalition=nil -local InitCategory=nil -local InitType=nil -local InitUnitCoalition=nil -local InitUnitCategory=nil -local InitUnitType=nil -local TargetUnit=nil -local TargetUNIT=nil -local TargetUnitName="" -local TargetGroup=nil -local TargetGroupName="" -local TargetPlayerName=nil -local TargetCoalition=nil -local TargetCategory=nil -local TargetType=nil -local TargetUnitCoalition=nil -local TargetUnitCategory=nil -local TargetUnitType=nil -if Event.IniDCSUnit then -InitUnit=Event.IniDCSUnit -InitUNIT=Event.IniUnit -InitUnitName=Event.IniDCSUnitName -InitGroup=Event.IniDCSGroup -InitGroupName=Event.IniDCSGroupName -InitPlayerName=Event.IniPlayerName -InitCoalition=Event.IniCoalition -InitCategory=Event.IniCategory -InitType=Event.IniTypeName -InitUnitCoalition=_SCORINGCoalition[InitCoalition] -InitUnitCategory=_SCORINGCategory[InitCategory] -InitUnitType=InitType -self:T({InitUnitName,InitGroupName,InitPlayerName,InitCoalition,InitCategory,InitType,InitUnitCoalition,InitUnitCategory,InitUnitType}) -end -if Event.TgtDCSUnit then -TargetUnit=Event.TgtDCSUnit -TargetUNIT=Event.TgtUnit -TargetUnitName=Event.TgtDCSUnitName -TargetGroup=Event.TgtDCSGroup -TargetGroupName=Event.TgtDCSGroupName -TargetPlayerName=Event.TgtPlayerName -TargetCoalition=Event.TgtCoalition -TargetCategory=Event.TgtCategory -TargetType=Event.TgtTypeName -TargetUnitCoalition=_SCORINGCoalition[TargetCoalition] -TargetUnitCategory=_SCORINGCategory[TargetCategory] -TargetUnitType=TargetType -self:T({TargetUnitName,TargetGroupName,TargetPlayerName,TargetCoalition,TargetCategory,TargetType,TargetUnitCoalition,TargetUnitCategory,TargetUnitType}) -end -if InitPlayerName~=nil then -self:_AddPlayerFromUnit(InitUNIT) -if self.Players[InitPlayerName]then -if TargetPlayerName~=nil then -self:_AddPlayerFromUnit(TargetUNIT) -end -self:T("Hitting Something") -if TargetCategory then -local Player=self.Players[InitPlayerName] -Player.Hit[TargetCategory]=Player.Hit[TargetCategory]or{} -Player.Hit[TargetCategory][TargetUnitName]=Player.Hit[TargetCategory][TargetUnitName]or{} -local PlayerHit=Player.Hit[TargetCategory][TargetUnitName] -PlayerHit.Score=PlayerHit.Score or 0 -PlayerHit.Penalty=PlayerHit.Penalty or 0 -PlayerHit.ScoreHit=PlayerHit.ScoreHit or 0 -PlayerHit.PenaltyHit=PlayerHit.PenaltyHit or 0 -PlayerHit.TimeStamp=PlayerHit.TimeStamp or 0 -PlayerHit.UNIT=PlayerHit.UNIT or TargetUNIT -PlayerHit.ThreatLevel,PlayerHit.ThreatType=PlayerHit.UNIT:GetThreatLevel() -if timer.getTime()-PlayerHit.TimeStamp>1 then -PlayerHit.TimeStamp=timer.getTime() -if TargetPlayerName~=nil then -Player.HitPlayers[TargetPlayerName]=true -end -local Score=0 -if InitCoalition then -if InitCoalition==TargetCoalition then -Player.Penalty=Player.Penalty+10 -PlayerHit.Penalty=PlayerHit.Penalty+10 -PlayerHit.PenaltyHit=PlayerHit.PenaltyHit+1 -if TargetPlayerName~=nil then -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit friendly player '"..TargetPlayerName.."' ".. -TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.PenaltyHit.." times. ".. -"Penalty: -"..PlayerHit.Penalty..". Score Total:"..Player.Score-Player.Penalty, -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -else -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit friendly target ".. -TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.PenaltyHit.." times. ".. -"Penalty: -"..PlayerHit.Penalty..". Score Total:"..Player.Score-Player.Penalty, -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -end -self:ScoreCSV(InitPlayerName,TargetPlayerName,"HIT_PENALTY",1,-10,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -else -Player.Score=Player.Score+1 -PlayerHit.Score=PlayerHit.Score+1 -PlayerHit.ScoreHit=PlayerHit.ScoreHit+1 -if TargetPlayerName~=nil then -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit enemy player '"..TargetPlayerName.."' ".. -TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.ScoreHit.." times. ".. -"Score: "..PlayerHit.Score..". Score Total:"..Player.Score-Player.Penalty, -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -else -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit enemy target ".. -TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.ScoreHit.." times. ".. -"Score: "..PlayerHit.Score..". Score Total:"..Player.Score-Player.Penalty, -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -end -self:ScoreCSV(InitPlayerName,TargetPlayerName,"HIT_SCORE",1,1,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -end -else -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit scenery object.", -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -self:ScoreCSV(InitPlayerName,"","HIT_SCORE",1,0,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,"","Scenery",TargetUnitType) -end -end -end -end -elseif InitPlayerName==nil then -end -if Event.WeaponPlayerName~=nil then -self:_AddPlayerFromUnit(Event.WeaponUNIT) -if self.Players[Event.WeaponPlayerName]then -if TargetPlayerName~=nil then -self:_AddPlayerFromUnit(TargetUNIT) -end -self:T("Hitting Scenery") -if TargetCategory then -local Player=self.Players[Event.WeaponPlayerName] -Player.Hit[TargetCategory]=Player.Hit[TargetCategory]or{} -Player.Hit[TargetCategory][TargetUnitName]=Player.Hit[TargetCategory][TargetUnitName]or{} -local PlayerHit=Player.Hit[TargetCategory][TargetUnitName] -PlayerHit.Score=PlayerHit.Score or 0 -PlayerHit.Penalty=PlayerHit.Penalty or 0 -PlayerHit.ScoreHit=PlayerHit.ScoreHit or 0 -PlayerHit.PenaltyHit=PlayerHit.PenaltyHit or 0 -PlayerHit.TimeStamp=PlayerHit.TimeStamp or 0 -PlayerHit.UNIT=PlayerHit.UNIT or TargetUNIT -PlayerHit.ThreatLevel,PlayerHit.ThreatType=PlayerHit.UNIT:GetThreatLevel() -if timer.getTime()-PlayerHit.TimeStamp>1 then -PlayerHit.TimeStamp=timer.getTime() -local Score=0 -if InitCoalition then -if InitCoalition==TargetCoalition then -Player.Penalty=Player.Penalty+10 -PlayerHit.Penalty=PlayerHit.Penalty+10 -PlayerHit.PenaltyHit=PlayerHit.PenaltyHit+1 -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..Event.WeaponPlayerName.."' hit friendly target ".. -TargetUnitCategory.." ( "..TargetType.." ) ".. -"Penalty: -"..PlayerHit.Penalty.." = "..Player.Score-Player.Penalty, -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(Event.WeaponCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -self:ScoreCSV(Event.WeaponPlayerName,TargetPlayerName,"HIT_PENALTY",1,-10,Event.WeaponName,Event.WeaponCoalition,Event.WeaponCategory,Event.WeaponTypeName,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -else -Player.Score=Player.Score+1 -PlayerHit.Score=PlayerHit.Score+1 -PlayerHit.ScoreHit=PlayerHit.ScoreHit+1 -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..Event.WeaponPlayerName.."' hit enemy target ".. -TargetUnitCategory.." ( "..TargetType.." ) ".. -"Score: +"..PlayerHit.Score.." = "..Player.Score-Player.Penalty, -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(Event.WeaponCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -self:ScoreCSV(Event.WeaponPlayerName,TargetPlayerName,"HIT_SCORE",1,1,Event.WeaponName,Event.WeaponCoalition,Event.WeaponCategory,Event.WeaponTypeName,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -end -else -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..Event.WeaponPlayerName.."' hit scenery object.", -MESSAGE.Type.Update -) -:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition()) -self:ScoreCSV(Event.WeaponPlayerName,"","HIT_SCORE",1,0,Event.WeaponName,Event.WeaponCoalition,Event.WeaponCategory,Event.WeaponTypeName,TargetUnitName,"","Scenery",TargetUnitType) -end -end -end -end -end -end -function SCORING:_EventOnDeadOrCrash(Event) -self:F({Event}) -local TargetUnit=nil -local TargetGroup=nil -local TargetUnitName="" -local TargetGroupName="" -local TargetPlayerName="" -local TargetCoalition=nil -local TargetCategory=nil -local TargetType=nil -local TargetUnitCoalition=nil -local TargetUnitCategory=nil -local TargetUnitType=nil -if Event.IniDCSUnit then -TargetUnit=Event.IniUnit -TargetUnitName=Event.IniDCSUnitName -TargetGroup=Event.IniDCSGroup -TargetGroupName=Event.IniDCSGroupName -TargetPlayerName=Event.IniPlayerName -TargetCoalition=Event.IniCoalition -TargetCategory=Event.IniCategory -TargetType=Event.IniTypeName -TargetUnitCoalition=_SCORINGCoalition[TargetCoalition] -TargetUnitCategory=_SCORINGCategory[TargetCategory] -TargetUnitType=TargetType -self:T({TargetUnitName,TargetGroupName,TargetPlayerName,TargetCoalition,TargetCategory,TargetType}) -end -for PlayerName,Player in pairs(self.Players)do -if Player then -self:T("Something got destroyed") -local InitUnitName=Player.UnitName -local InitUnitType=Player.UnitType -local InitCoalition=Player.UnitCoalition -local InitCategory=Player.UnitCategory -local InitUnitCoalition=_SCORINGCoalition[InitCoalition] -local InitUnitCategory=_SCORINGCategory[InitCategory] -self:T({InitUnitName,InitUnitType,InitUnitCoalition,InitCoalition,InitUnitCategory,InitCategory}) -local Destroyed=false -if Player and Player.Hit and Player.Hit[TargetCategory]and Player.Hit[TargetCategory][TargetUnitName]and Player.Hit[TargetCategory][TargetUnitName].TimeStamp~=0 then -local TargetThreatLevel=Player.Hit[TargetCategory][TargetUnitName].ThreatLevel -local TargetThreatType=Player.Hit[TargetCategory][TargetUnitName].ThreatType -Player.Destroy[TargetCategory]=Player.Destroy[TargetCategory]or{} -Player.Destroy[TargetCategory][TargetType]=Player.Destroy[TargetCategory][TargetType]or{} -local TargetDestroy=Player.Destroy[TargetCategory][TargetType] -TargetDestroy.Score=TargetDestroy.Score or 0 -TargetDestroy.ScoreDestroy=TargetDestroy.ScoreDestroy or 0 -TargetDestroy.Penalty=TargetDestroy.Penalty or 0 -TargetDestroy.PenaltyDestroy=TargetDestroy.PenaltyDestroy or 0 -if TargetCoalition then -if InitCoalition==TargetCoalition then -local ThreatLevelTarget=TargetThreatLevel -local ThreatTypeTarget=TargetThreatType -local ThreatLevelPlayer=Player.ThreatLevel/10+1 -local ThreatPenalty=math.ceil((ThreatLevelTarget/ThreatLevelPlayer)*self.ScaleDestroyPenalty/10) -self:F({ThreatLevel=ThreatPenalty,ThreatLevelTarget=ThreatLevelTarget,ThreatTypeTarget=ThreatTypeTarget,ThreatLevelPlayer=ThreatLevelPlayer}) -Player.Penalty=Player.Penalty+ThreatPenalty -TargetDestroy.Penalty=TargetDestroy.Penalty+ThreatPenalty -TargetDestroy.PenaltyDestroy=TargetDestroy.PenaltyDestroy+1 -if Player.HitPlayers[TargetPlayerName]then -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed friendly player '"..TargetPlayerName.."' ".. -TargetUnitCategory.." ( "..ThreatTypeTarget.." ) ".. -"Penalty: -"..TargetDestroy.Penalty.." = "..Player.Score-Player.Penalty, -MESSAGE.Type.Information -) -:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition()) -else -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed friendly target ".. -TargetUnitCategory.." ( "..ThreatTypeTarget.." ) ".. -"Penalty: -"..TargetDestroy.Penalty.." = "..Player.Score-Player.Penalty, -MESSAGE.Type.Information -) -:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition()) -end -Destroyed=true -self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_PENALTY",1,ThreatPenalty,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -else -local ThreatLevelTarget=TargetThreatLevel -local ThreatTypeTarget=TargetThreatType -local ThreatLevelPlayer=Player.ThreatLevel/10+1 -local ThreatScore=math.ceil((ThreatLevelTarget/ThreatLevelPlayer)*self.ScaleDestroyScore/10) -self:F({ThreatLevel=ThreatScore,ThreatLevelTarget=ThreatLevelTarget,ThreatTypeTarget=ThreatTypeTarget,ThreatLevelPlayer=ThreatLevelPlayer}) -Player.Score=Player.Score+ThreatScore -TargetDestroy.Score=TargetDestroy.Score+ThreatScore -TargetDestroy.ScoreDestroy=TargetDestroy.ScoreDestroy+1 -if Player.HitPlayers[TargetPlayerName]then -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed enemy player '"..TargetPlayerName.."' ".. -TargetUnitCategory.." ( "..ThreatTypeTarget.." ) ".. -"Score: +"..TargetDestroy.Score.." = "..Player.Score-Player.Penalty, -MESSAGE.Type.Information -) -:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition()) -else -MESSAGE -:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed enemy ".. -TargetUnitCategory.." ( "..ThreatTypeTarget.." ) ".. -"Score: +"..TargetDestroy.Score.." = "..Player.Score-Player.Penalty, -MESSAGE.Type.Information -) -:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition()) -end -Destroyed=true -self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_SCORE",1,ThreatScore,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -local UnitName=TargetUnit:GetName() -local Score=self.ScoringObjects[UnitName] -if Score then -Player.Score=Player.Score+Score -TargetDestroy.Score=TargetDestroy.Score+Score -MESSAGE -:NewType(self.DisplayMessagePrefix.."Special target '"..TargetUnitCategory.." ( "..ThreatTypeTarget.." ) ".." destroyed! ".. -"Player '"..PlayerName.."' receives an extra "..Score.." points! Total: "..Player.Score-Player.Penalty, -MESSAGE.Type.Information -) -:ToAllIf(self:IfMessagesScore()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesScore()and self:IfMessagesToCoalition()) -self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_SCORE",1,Score,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -Destroyed=true -end -for ZoneName,ScoreZoneData in pairs(self.ScoringZones)do -self:F({ScoringZone=ScoreZoneData}) -local ScoreZone=ScoreZoneData.ScoreZone -local Score=ScoreZoneData.Score -if ScoreZone:IsVec2InZone(TargetUnit:GetVec2())then -Player.Score=Player.Score+Score -TargetDestroy.Score=TargetDestroy.Score+Score -MESSAGE -:NewType(self.DisplayMessagePrefix.."Target destroyed in zone '"..ScoreZone:GetName().."'.".. -"Player '"..PlayerName.."' receives an extra "..Score.." points! ".. -"Total: "..Player.Score-Player.Penalty, -MESSAGE.Type.Information) -:ToAllIf(self:IfMessagesZone()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesZone()and self:IfMessagesToCoalition()) -self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_SCORE",1,Score,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -Destroyed=true -end -end -end -else -for ZoneName,ScoreZoneData in pairs(self.ScoringZones)do -self:F({ScoringZone=ScoreZoneData}) -local ScoreZone=ScoreZoneData.ScoreZone -local Score=ScoreZoneData.Score -if ScoreZone:IsVec2InZone(TargetUnit:GetVec2())then -Player.Score=Player.Score+Score -TargetDestroy.Score=TargetDestroy.Score+Score -MESSAGE -:NewType(self.DisplayMessagePrefix.."Scenery destroyed in zone '"..ScoreZone:GetName().."'.".. -"Player '"..PlayerName.."' receives an extra "..Score.." points! ".. -"Total: "..Player.Score-Player.Penalty, -MESSAGE.Type.Information -) -:ToAllIf(self:IfMessagesZone()and self:IfMessagesToAll()) -:ToCoalitionIf(InitCoalition,self:IfMessagesZone()and self:IfMessagesToCoalition()) -Destroyed=true -self:ScoreCSV(PlayerName,"","DESTROY_SCORE",1,Score,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,"","Scenery",TargetUnitType) -end -end -end -if Destroyed then -Player.Hit[TargetCategory][TargetUnitName].TimeStamp=0 -end -end -end -end -end -function SCORING:ReportDetailedPlayerHits(PlayerName) -local ScoreMessage="" -local PlayerScore=0 -local PlayerPenalty=0 -local PlayerData=self.Players[PlayerName] -if PlayerData then -self:T("Score Player: "..PlayerName) -local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition] -local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory] -local InitUnitType=PlayerData.UnitType -local InitUnitName=PlayerData.UnitName -local ScoreMessageHits="" -for CategoryID,CategoryName in pairs(_SCORINGCategory)do -self:T(CategoryName) -if PlayerData.Hit[CategoryID]then -self:T("Hit scores exist for player "..PlayerName) -local Score=0 -local ScoreHit=0 -local Penalty=0 -local PenaltyHit=0 -for UnitName,UnitData in pairs(PlayerData.Hit[CategoryID])do -Score=Score+UnitData.Score -ScoreHit=ScoreHit+UnitData.ScoreHit -Penalty=Penalty+UnitData.Penalty -PenaltyHit=UnitData.PenaltyHit -end -local ScoreMessageHit=string.format("%s:%d ",CategoryName,Score-Penalty) -self:T(ScoreMessageHit) -ScoreMessageHits=ScoreMessageHits..ScoreMessageHit -PlayerScore=PlayerScore+Score -PlayerPenalty=PlayerPenalty+Penalty -else -end -end -if ScoreMessageHits~=""then -ScoreMessage="Hits: "..ScoreMessageHits -end -end -return ScoreMessage,PlayerScore,PlayerPenalty -end -function SCORING:ReportDetailedPlayerDestroys(PlayerName) -local ScoreMessage="" -local PlayerScore=0 -local PlayerPenalty=0 -local PlayerData=self.Players[PlayerName] -if PlayerData then -self:T("Score Player: "..PlayerName) -local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition] -local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory] -local InitUnitType=PlayerData.UnitType -local InitUnitName=PlayerData.UnitName -local ScoreMessageDestroys="" -for CategoryID,CategoryName in pairs(_SCORINGCategory)do -if PlayerData.Destroy[CategoryID]then -self:T("Destroy scores exist for player "..PlayerName) -local Score=0 -local ScoreDestroy=0 -local Penalty=0 -local PenaltyDestroy=0 -for UnitName,UnitData in pairs(PlayerData.Destroy[CategoryID])do -self:F({UnitData=UnitData}) -if UnitData~={}then -Score=Score+UnitData.Score -ScoreDestroy=ScoreDestroy+UnitData.ScoreDestroy -Penalty=Penalty+UnitData.Penalty -PenaltyDestroy=PenaltyDestroy+UnitData.PenaltyDestroy -end -end -local ScoreMessageDestroy=string.format(" %s:%d ",CategoryName,Score-Penalty) -self:T(ScoreMessageDestroy) -ScoreMessageDestroys=ScoreMessageDestroys..ScoreMessageDestroy -PlayerScore=PlayerScore+Score -PlayerPenalty=PlayerPenalty+Penalty -else -end -end -if ScoreMessageDestroys~=""then -ScoreMessage="Destroys: "..ScoreMessageDestroys -end -end -return ScoreMessage,PlayerScore,PlayerPenalty -end -function SCORING:ReportDetailedPlayerCoalitionChanges(PlayerName) -local ScoreMessage="" -local PlayerScore=0 -local PlayerPenalty=0 -local PlayerData=self.Players[PlayerName] -if PlayerData then -self:T("Score Player: "..PlayerName) -local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition] -local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory] -local InitUnitType=PlayerData.UnitType -local InitUnitName=PlayerData.UnitName -local ScoreMessageCoalitionChangePenalties="" -if PlayerData.PenaltyCoalition~=0 then -ScoreMessageCoalitionChangePenalties=ScoreMessageCoalitionChangePenalties..string.format(" -%d (%d changed)",PlayerData.Penalty,PlayerData.PenaltyCoalition) -PlayerPenalty=PlayerPenalty+PlayerData.Penalty -end -if ScoreMessageCoalitionChangePenalties~=""then -ScoreMessage=ScoreMessage.."Coalition Penalties: "..ScoreMessageCoalitionChangePenalties -end -end -return ScoreMessage,PlayerScore,PlayerPenalty -end -function SCORING:ReportDetailedPlayerGoals(PlayerName) -local ScoreMessage="" -local PlayerScore=0 -local PlayerPenalty=0 -local PlayerData=self.Players[PlayerName] -if PlayerData then -self:T("Score Player: "..PlayerName) -local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition] -local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory] -local InitUnitType=PlayerData.UnitType -local InitUnitName=PlayerData.UnitName -local ScoreMessageGoal="" -local ScoreGoal=0 -local ScoreTask=0 -for GoalName,GoalData in pairs(PlayerData.Goals)do -ScoreGoal=ScoreGoal+GoalData.Score -ScoreMessageGoal=ScoreMessageGoal.."'"..GoalName.."':"..GoalData.Score.."; " -end -PlayerScore=PlayerScore+ScoreGoal -if ScoreMessageGoal~=""then -ScoreMessage="Goals: "..ScoreMessageGoal -end -end -return ScoreMessage,PlayerScore,PlayerPenalty -end -function SCORING:ReportDetailedPlayerMissions(PlayerName) -local ScoreMessage="" -local PlayerScore=0 -local PlayerPenalty=0 -local PlayerData=self.Players[PlayerName] -if PlayerData then -self:T("Score Player: "..PlayerName) -local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition] -local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory] -local InitUnitType=PlayerData.UnitType -local InitUnitName=PlayerData.UnitName -local ScoreMessageMission="" -local ScoreMission=0 -local ScoreTask=0 -for MissionName,MissionData in pairs(PlayerData.Mission)do -ScoreMission=ScoreMission+MissionData.ScoreMission -ScoreTask=ScoreTask+MissionData.ScoreTask -ScoreMessageMission=ScoreMessageMission.."'"..MissionName.."'; " -end -PlayerScore=PlayerScore+ScoreMission+ScoreTask -if ScoreMessageMission~=""then -ScoreMessage="Tasks: "..ScoreTask.." Mission: "..ScoreMission.." ( "..ScoreMessageMission..")" -end -end -return ScoreMessage,PlayerScore,PlayerPenalty -end -function SCORING:ReportScoreGroupSummary(PlayerGroup) -local PlayerMessage="" -self:T("Report Score Group Summary") -local PlayerUnits=PlayerGroup:GetUnits() -for UnitID,PlayerUnit in pairs(PlayerUnits)do -local PlayerUnit=PlayerUnit -local PlayerName=PlayerUnit:GetPlayerName() -if PlayerName then -local ReportHits,ScoreHits,PenaltyHits=self:ReportDetailedPlayerHits(PlayerName) -ReportHits=ReportHits~=""and"\n- "..ReportHits or ReportHits -self:F({ReportHits,ScoreHits,PenaltyHits}) -local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName) -ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys -self:F({ReportDestroys,ScoreDestroys,PenaltyDestroys}) -local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName) -ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges -self:F({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges}) -local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName) -ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals -self:F({ReportGoals,ScoreGoals,PenaltyGoals}) -local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName) -ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions -self:F({ReportMissions,ScoreMissions,PenaltyMissions}) -local PlayerScore=ScoreHits+ScoreDestroys+ScoreCoalitionChanges+ScoreGoals+ScoreMissions -local PlayerPenalty=PenaltyHits+PenaltyDestroys+PenaltyCoalitionChanges+PenaltyGoals+PenaltyMissions -PlayerMessage= -string.format("Player '%s' Score = %d ( %d Score, -%d Penalties )", -PlayerName, -PlayerScore-PlayerPenalty, -PlayerScore, -PlayerPenalty -) -MESSAGE:NewType(PlayerMessage,MESSAGE.Type.Detailed):ToGroup(PlayerGroup) -end -end -end -function SCORING:ReportScoreGroupDetailed(PlayerGroup) -local PlayerMessage="" -self:T("Report Score Group Detailed") -local PlayerUnits=PlayerGroup:GetUnits() -for UnitID,PlayerUnit in pairs(PlayerUnits)do -local PlayerUnit=PlayerUnit -local PlayerName=PlayerUnit:GetPlayerName() -if PlayerName then -local ReportHits,ScoreHits,PenaltyHits=self:ReportDetailedPlayerHits(PlayerName) -ReportHits=ReportHits~=""and"\n- "..ReportHits or ReportHits -self:F({ReportHits,ScoreHits,PenaltyHits}) -local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName) -ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys -self:F({ReportDestroys,ScoreDestroys,PenaltyDestroys}) -local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName) -ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges -self:F({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges}) -local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName) -ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals -self:F({ReportGoals,ScoreGoals,PenaltyGoals}) -local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName) -ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions -self:F({ReportMissions,ScoreMissions,PenaltyMissions}) -local PlayerScore=ScoreHits+ScoreDestroys+ScoreCoalitionChanges+ScoreGoals+ScoreMissions -local PlayerPenalty=PenaltyHits+PenaltyDestroys+PenaltyCoalitionChanges+ScoreGoals+PenaltyMissions -PlayerMessage= -string.format("Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", -PlayerName, -PlayerScore-PlayerPenalty, -PlayerScore, -PlayerPenalty, -ReportHits, -ReportDestroys, -ReportCoalitionChanges, -ReportGoals, -ReportMissions -) -MESSAGE:NewType(PlayerMessage,MESSAGE.Type.Detailed):ToGroup(PlayerGroup) -end -end -end -function SCORING:ReportScoreAllSummary(PlayerGroup) -local PlayerMessage="" -self:T({"Summary Score Report of All Players",Players=self.Players}) -for PlayerName,PlayerData in pairs(self.Players)do -self:T({PlayerName=PlayerName,PlayerGroup=PlayerGroup}) -if PlayerName then -local ReportHits,ScoreHits,PenaltyHits=self:ReportDetailedPlayerHits(PlayerName) -ReportHits=ReportHits~=""and"\n- "..ReportHits or ReportHits -self:F({ReportHits,ScoreHits,PenaltyHits}) -local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName) -ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys -self:F({ReportDestroys,ScoreDestroys,PenaltyDestroys}) -local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName) -ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges -self:F({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges}) -local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName) -ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals -self:F({ReportGoals,ScoreGoals,PenaltyGoals}) -local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName) -ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions -self:F({ReportMissions,ScoreMissions,PenaltyMissions}) -local PlayerScore=ScoreHits+ScoreDestroys+ScoreCoalitionChanges+ScoreGoals+ScoreMissions -local PlayerPenalty=PenaltyHits+PenaltyDestroys+PenaltyCoalitionChanges+ScoreGoals+PenaltyMissions -PlayerMessage= -string.format("Player '%s' Score = %d ( %d Score, -%d Penalties )", -PlayerName, -PlayerScore-PlayerPenalty, -PlayerScore, -PlayerPenalty -) -MESSAGE:NewType(PlayerMessage,MESSAGE.Type.Overview):ToGroup(PlayerGroup) -end -end -end -function SCORING:SecondsToClock(sSeconds) -local nSeconds=sSeconds -if nSeconds==0 then -return"00:00:00"; -else -nHours=string.format("%02.f",math.floor(nSeconds/3600)); -nMins=string.format("%02.f",math.floor(nSeconds/60-(nHours*60))); -nSecs=string.format("%02.f",math.floor(nSeconds-nHours*3600-nMins*60)); -return nHours..":"..nMins..":"..nSecs -end -end -function SCORING:OpenCSV(ScoringCSV) -self:F(ScoringCSV) -if lfs and io and os then -if ScoringCSV then -self.ScoringCSV=ScoringCSV -local fdir=lfs.writedir()..[[Logs\]]..self.ScoringCSV.." "..os.date("%Y-%m-%d %H-%M-%S")..".csv" -self.CSVFile,self.err=io.open(fdir,"w+") -if not self.CSVFile then -error("Error: Cannot open CSV file in "..lfs.writedir()) -end -self.CSVFile:write('"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n') -self.RunTime=os.date("%y-%m-%d_%H-%M-%S") -else -error("A string containing the CSV file name must be given.") -end -else -self:F("The MissionScripting.lua file has not been changed to allow lfs, io and os modules to be used...") -end -return self -end -function SCORING:ScoreCSV(PlayerName,TargetPlayerName,ScoreType,ScoreTimes,ScoreAmount,PlayerUnitName,PlayerUnitCoalition,PlayerUnitCategory,PlayerUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType) -local ScoreTime=self:SecondsToClock(timer.getTime()) -PlayerName=PlayerName:gsub('"','_') -TargetPlayerName=TargetPlayerName or"" -TargetPlayerName=TargetPlayerName:gsub('"','_') -if PlayerUnitName and PlayerUnitName~=''then -local PlayerUnit=Unit.getByName(PlayerUnitName) -if PlayerUnit then -if not PlayerUnitCategory then -PlayerUnitCategory=_SCORINGCategory[PlayerUnit:getDesc().category] -end -if not PlayerUnitCoalition then -PlayerUnitCoalition=_SCORINGCoalition[PlayerUnit:getCoalition()] -end -if not PlayerUnitType then -PlayerUnitType=PlayerUnit:getTypeName() -end -else -PlayerUnitName='' -PlayerUnitCategory='' -PlayerUnitCoalition='' -PlayerUnitType='' -end -else -PlayerUnitName='' -PlayerUnitCategory='' -PlayerUnitCoalition='' -PlayerUnitType='' -end -TargetUnitCoalition=TargetUnitCoalition or"" -TargetUnitCategory=TargetUnitCategory or"" -TargetUnitType=TargetUnitType or"" -TargetUnitName=TargetUnitName or"" -if lfs and io and os then -self.CSVFile:write( -'"'..self.GameName..'"'..','.. -'"'..self.RunTime..'"'..','.. -''..ScoreTime..''..','.. -'"'..PlayerName..'"'..','.. -'"'..TargetPlayerName..'"'..','.. -'"'..ScoreType..'"'..','.. -'"'..PlayerUnitCoalition..'"'..','.. -'"'..PlayerUnitCategory..'"'..','.. -'"'..PlayerUnitType..'"'..','.. -'"'..PlayerUnitName..'"'..','.. -'"'..TargetUnitCoalition..'"'..','.. -'"'..TargetUnitCategory..'"'..','.. -'"'..TargetUnitType..'"'..','.. -'"'..TargetUnitName..'"'..','.. -''..ScoreTimes..''..','.. -''..ScoreAmount -) -self.CSVFile:write("\n") -end -end -function SCORING:CloseCSV() -if lfs and io and os then -self.CSVFile:close() -end -end -CLEANUP_AIRBASE={ -ClassName="CLEANUP_AIRBASE", -TimeInterval=0.2, -CleanUpList={}, -} -CLEANUP_AIRBASE.__={} -CLEANUP_AIRBASE.__.Airbases={} -function CLEANUP_AIRBASE:New(AirbaseNames) -local self=BASE:Inherit(self,BASE:New()) -self:F({AirbaseNames}) -if type(AirbaseNames)=='table'then -for AirbaseID,AirbaseName in pairs(AirbaseNames)do -self:AddAirbase(AirbaseName) -end -else -local AirbaseName=AirbaseNames -self:AddAirbase(AirbaseName) -end -self:HandleEvent(EVENTS.Birth,self.__.OnEventBirth) -self.__.CleanUpScheduler=SCHEDULER:New(self,self.__.CleanUpSchedule,{},1,self.TimeInterval) -self:HandleEvent(EVENTS.EngineShutdown,self.__.EventAddForCleanUp) -self:HandleEvent(EVENTS.EngineStartup,self.__.EventAddForCleanUp) -self:HandleEvent(EVENTS.Hit,self.__.EventAddForCleanUp) -self:HandleEvent(EVENTS.PilotDead,self.__.OnEventCrash) -self:HandleEvent(EVENTS.Dead,self.__.OnEventCrash) -self:HandleEvent(EVENTS.Crash,self.__.OnEventCrash) -for UnitName,Unit in pairs(_DATABASE.UNITS)do -local Unit=Unit -if Unit:IsAlive()~=nil then -if self:IsInAirbase(Unit:GetVec2())then -self:F({UnitName=UnitName}) -self.CleanUpList[UnitName]={} -self.CleanUpList[UnitName].CleanUpUnit=Unit -self.CleanUpList[UnitName].CleanUpGroup=Unit:GetGroup() -self.CleanUpList[UnitName].CleanUpGroupName=Unit:GetGroup():GetName() -self.CleanUpList[UnitName].CleanUpUnitName=Unit:GetName() -end -end -end -return self -end -function CLEANUP_AIRBASE:AddAirbase(AirbaseName) -self.__.Airbases[AirbaseName]=AIRBASE:FindByName(AirbaseName) -self:F({"Airbase:",AirbaseName,self.__.Airbases[AirbaseName]:GetDesc()}) -return self -end -function CLEANUP_AIRBASE:RemoveAirbase(AirbaseName) -self.__.Airbases[AirbaseName]=nil -return self -end -function CLEANUP_AIRBASE:SetCleanMissiles(CleanMissiles) -if CleanMissiles then -self:HandleEvent(EVENTS.Shot,self.__.OnEventShot) -else -self:UnHandleEvent(EVENTS.Shot) -end -end -function CLEANUP_AIRBASE.__:IsInAirbase(Vec2) -local InAirbase=false -for AirbaseName,Airbase in pairs(self.__.Airbases)do -local Airbase=Airbase -if Airbase:GetZone():IsVec2InZone(Vec2)then -InAirbase=true -break; -end -end -return InAirbase -end -function CLEANUP_AIRBASE.__:DestroyUnit(CleanUpUnit) -self:F({CleanUpUnit}) -if CleanUpUnit then -local CleanUpUnitName=CleanUpUnit:GetName() -local CleanUpGroup=CleanUpUnit:GetGroup() -if CleanUpGroup:IsAlive()then -local CleanUpGroupUnits=CleanUpGroup:GetUnits() -if#CleanUpGroupUnits==1 then -local CleanUpGroupName=CleanUpGroup:GetName() -CleanUpGroup:Destroy() -else -CleanUpUnit:Destroy() -end -self.CleanUpList[CleanUpUnitName]=nil -end -end -end -function CLEANUP_AIRBASE.__:DestroyMissile(MissileObject) -self:F({MissileObject}) -if MissileObject and MissileObject:isExist()then -MissileObject:destroy() -self:T("MissileObject Destroyed") -end -end -function CLEANUP_AIRBASE.__:OnEventBirth(EventData) -self:F({EventData}) -if EventData and EventData.IniUnit and EventData.IniUnit:IsAlive()~=nil then -if self:IsInAirbase(EventData.IniUnit:GetVec2())then -self.CleanUpList[EventData.IniDCSUnitName]={} -self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit=EventData.IniUnit -self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup=EventData.IniGroup -self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName=EventData.IniDCSGroupName -self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName=EventData.IniDCSUnitName -end -end -end -function CLEANUP_AIRBASE.__:OnEventCrash(Event) -self:F({Event}) -if Event.IniDCSUnitName and Event.IniCategory==Object.Category.UNIT then -self.CleanUpList[Event.IniDCSUnitName]={} -self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit=Event.IniUnit -self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup=Event.IniGroup -self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName=Event.IniDCSGroupName -self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName=Event.IniDCSUnitName -end -end -function CLEANUP_AIRBASE.__:OnEventShot(Event) -self:F({Event}) -if self:IsInAirbase(Event.IniUnit:GetVec2())then -self:DestroyMissile(Event.Weapon) -end -end -function CLEANUP_AIRBASE.__:OnEventHit(Event) -self:F({Event}) -if Event.IniUnit then -if self:IsInAirbase(Event.IniUnit:GetVec2())then -self:T({"Life: ",Event.IniDCSUnitName,' = ',Event.IniUnit:GetLife(),"/",Event.IniUnit:GetLife0()}) -if Event.IniUnit:GetLife()0 then -local MoveProbability=(self.MoveMaximum*100)/self.AliveUnits -self:T('Move Probability = '..MoveProbability) -for MovementUnitName,MovementGroupName in pairs(self.MoveUnits)do -local MovementGroup=Group.getByName(MovementGroupName) -if MovementGroup and MovementGroup:isExist()then -local MoveOrStop=math.random(1,100) -self:T('MoveOrStop = '..MoveOrStop) -if MoveOrStop<=MoveProbability then -self:T('Group continues moving = '..MovementGroupName) -trigger.action.groupContinueMoving(MovementGroup) -else -self:T('Group stops moving = '..MovementGroupName) -trigger.action.groupStopMoving(MovementGroup) -end -else -self.MoveUnits[MovementUnitName]=nil -end -end -end -return true -end -SEAD={ -ClassName="SEAD", -TargetSkill={ -Average={Evade=30,DelayOn={40,60}}, -Good={Evade=20,DelayOn={30,50}}, -High={Evade=15,DelayOn={20,40}}, -Excellent={Evade=10,DelayOn={10,30}} -}, -SEADGroupPrefixes={}, -SuppressedGroups={}, -EngagementRange=75 -} -SEAD.Harms={ -["AGM_88"]="AGM_88", -["AGM_45"]="AGM_45", -["AGM_122"]="AGM_122", -["AGM_84"]="AGM_84", -["AGM_45"]="AGM_45", -["ALARN"]="ALARM", -["LD-10"]="LD-10", -["X_58"]="X_58", -["X_28"]="X_28", -["X_25"]="X_25", -["X_31"]="X_31", -["Kh25"]="Kh25", -} -function SEAD:New(SEADGroupPrefixes) -local self=BASE:Inherit(self,BASE:New()) -self:F(SEADGroupPrefixes) -if type(SEADGroupPrefixes)=='table'then -for SEADGroupPrefixID,SEADGroupPrefix in pairs(SEADGroupPrefixes)do -self.SEADGroupPrefixes[SEADGroupPrefix]=SEADGroupPrefix -end -else -self.SEADGroupPrefixes[SEADGroupPrefixes]=SEADGroupPrefixes -end -self:HandleEvent(EVENTS.Shot) -self:I("*** SEAD - Started Version 0.2.7") -return self -end -function SEAD:UpdateSet(SEADGroupPrefixes) -self:F(SEADGroupPrefixes) -if type(SEADGroupPrefixes)=='table'then -for SEADGroupPrefixID,SEADGroupPrefix in pairs(SEADGroupPrefixes)do -self.SEADGroupPrefixes[SEADGroupPrefix]=SEADGroupPrefix -end -else -self.SEADGroupPrefixes[SEADGroupPrefixes]=SEADGroupPrefixes -end -return self -end -function SEAD:SetEngagementRange(range) -self:F({range}) -range=range or 75 -if range<0 or range>100 then -range=75 -end -self.EngagementRange=range -self:T(string.format("*** SEAD - Engagement range set to %s",range)) -return self -end -function SEAD:_CheckHarms(WeaponName) -self:F({WeaponName}) -local hit=false -for _,_name in pairs(SEAD.Harms)do -if string.find(WeaponName,_name,1)then hit=true end -end -return hit -end -function SEAD:OnEventShot(EventData) -self:T({EventData}) -local SEADUnit=EventData.IniDCSUnit -local SEADUnitName=EventData.IniDCSUnitName -local SEADWeapon=EventData.Weapon -local SEADWeaponName=EventData.WeaponName -self:T("*** SEAD - Missile Launched = "..SEADWeaponName) -self:T({SEADWeapon}) -if self:_CheckHarms(SEADWeaponName)then -local _targetskill="Random" -local _targetMimgroupName="none" -local _evade=math.random(1,100) -local _targetMim=EventData.Weapon:getTarget() -local _targetUnit=UNIT:Find(_targetMim) -if _targetUnit and _targetUnit:IsAlive()then -local _targetMimgroup=_targetUnit:GetGroup() -local _targetMimgroupName=_targetMimgroup:GetName() -self:T(self.SEADGroupPrefixes) -self:T(_targetMimgroupName) -end -local SEADGroupFound=false -for SEADGroupPrefixID,SEADGroupPrefix in pairs(self.SEADGroupPrefixes)do -if string.find(_targetMimgroupName,SEADGroupPrefix,1,true)then -SEADGroupFound=true -self:T('*** SEAD - Group Found') -break -end -end -if SEADGroupFound==true then -if _targetskill=="Random"then -local Skills={"Average","Good","High","Excellent"} -_targetskill=Skills[math.random(1,4)] -end -self:T(_targetskill) -if self.TargetSkill[_targetskill]then -if(_evade>self.TargetSkill[_targetskill].Evade)then -self:T(string.format("*** SEAD - Evading, target skill "..string.format(_targetskill))) -local _targetMimgroup=Unit.getGroup(Weapon.getTarget(SEADWeapon)) -local _targetMimcont=_targetMimgroup:getController() -routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -local id={ -groupName=_targetMimgroup, -ctrl=_targetMimcont -} -local function SuppressionEnd(id) -local range=self.EngagementRange -self:T(string.format("*** SEAD - Engagement Range is %d",range)) -id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED) -id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) -self.SuppressedGroups[id.groupName]=nil -end -local delay=math.random(self.TargetSkill[_targetskill].DelayOn[1],self.TargetSkill[_targetskill].DelayOn[2]) -local SuppressionEndTime=timer.getTime()+delay -if self.SuppressedGroups[id.groupName]==nil then -self.SuppressedGroups[id.groupName]={ -SuppressionEndTime=delay -} -Controller.setOption(_targetMimcont,AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) -timer.scheduleFunction(SuppressionEnd,id,SuppressionEndTime) -end -end -end -end -end -end -ESCORT={ -ClassName="ESCORT", -EscortName=nil, -EscortClient=nil, -EscortGroup=nil, -EscortMode=1, -MODE={ -FOLLOW=1, -MISSION=2, -}, -Targets={}, -FollowScheduler=nil, -ReportTargets=true, -OptionROE=AI.Option.Air.val.ROE.OPEN_FIRE, -OptionReactionOnThreat=AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION, -SmokeDirectionVector=false, -TaskPoints={} -} -function ESCORT:New(EscortClient,EscortGroup,EscortName,EscortBriefing) -local self=BASE:Inherit(self,BASE:New()) -self:F({EscortClient,EscortGroup,EscortName}) -self.EscortClient=EscortClient -self.EscortGroup=EscortGroup -self.EscortName=EscortName -self.EscortBriefing=EscortBriefing -self.EscortSetGroup=SET_GROUP:New() -self.EscortSetGroup:AddObject(self.EscortGroup) -self.EscortSetGroup:Flush() -self.Detection=DETECTION_UNITS:New(self.EscortSetGroup,15000) -self.EscortGroup.Detection=self.Detection -if not self.EscortClient._EscortGroups then -self.EscortClient._EscortGroups={} -end -if not self.EscortClient._EscortGroups[EscortGroup:GetName()]then -self.EscortClient._EscortGroups[EscortGroup:GetName()]={} -self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup=self.EscortGroup -self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName=self.EscortName -self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection=self.EscortGroup.Detection -end -self.EscortMenu=MENU_GROUP:New(self.EscortClient:GetGroup(),self.EscortName) -self.EscortGroup:WayPointInitialize(1) -self.EscortGroup:OptionROTVertical() -self.EscortGroup:OptionROEOpenFire() -if not EscortBriefing then -EscortGroup:MessageToClient(EscortGroup:GetCategoryName().." '"..EscortName.."' ("..EscortGroup:GetCallsign()..") reporting! ".. -"We're escorting your flight. ".. -"Use the Radio Menu and F10 and use the options under + "..EscortName.."\n", -60,EscortClient -) -else -EscortGroup:MessageToClient(EscortGroup:GetCategoryName().." '"..EscortName.."' ("..EscortGroup:GetCallsign()..") "..EscortBriefing, -60,EscortClient -) -end -self.FollowDistance=100 -self.CT1=0 -self.GT1=0 -self.FollowScheduler,self.FollowSchedule=SCHEDULER:New(self,self._FollowScheduler,{},1,.5,.01) -self.FollowScheduler:Stop(self.FollowSchedule) -self.EscortMode=ESCORT.MODE.MISSION -return self -end -function ESCORT:SetDetection(Detection) -self.Detection=Detection -self.EscortGroup.Detection=self.Detection -self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection=self.EscortGroup.Detection -Detection:__Start(1) -end -function ESCORT:TestSmokeDirectionVector(SmokeDirection) -self.SmokeDirectionVector=(SmokeDirection==true)and true or false -end -function ESCORT:Menus() -self:F() -self:MenuFollowAt(100) -self:MenuFollowAt(200) -self:MenuFollowAt(300) -self:MenuFollowAt(400) -self:MenuScanForTargets(100,60) -self:MenuHoldAtEscortPosition(30) -self:MenuHoldAtLeaderPosition(30) -self:MenuFlare() -self:MenuSmoke() -self:MenuReportTargets(60) -self:MenuAssistedAttack() -self:MenuROE() -self:MenuEvasion() -self:MenuResumeMission() -return self -end -function ESCORT:MenuFollowAt(Distance) -self:F(Distance) -if self.EscortGroup:IsAir()then -if not self.EscortMenuReportNavigation then -self.EscortMenuReportNavigation=MENU_GROUP:New(self.EscortClient:GetGroup(),"Navigation",self.EscortMenu) -end -if not self.EscortMenuJoinUpAndFollow then -self.EscortMenuJoinUpAndFollow={} -end -self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1]=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Join-Up and Follow at "..Distance,self.EscortMenuReportNavigation,ESCORT._JoinUpAndFollow,self,Distance) -self.EscortMode=ESCORT.MODE.FOLLOW -end -return self -end -function ESCORT:MenuHoldAtEscortPosition(Height,Seconds,MenuTextFormat) -self:F({Height,Seconds,MenuTextFormat}) -if self.EscortGroup:IsAir()then -if not self.EscortMenuHold then -self.EscortMenuHold=MENU_GROUP:New(self.EscortClient:GetGroup(),"Hold position",self.EscortMenu) -end -if not Height then -Height=30 -end -if not Seconds then -Seconds=0 -end -local MenuText="" -if not MenuTextFormat then -if Seconds==0 then -MenuText=string.format("Hold at %d meter",Height) -else -MenuText=string.format("Hold at %d meter for %d seconds",Height,Seconds) -end -else -if Seconds==0 then -MenuText=string.format(MenuTextFormat,Height) -else -MenuText=string.format(MenuTextFormat,Height,Seconds) -end -end -if not self.EscortMenuHoldPosition then -self.EscortMenuHoldPosition={} -end -self.EscortMenuHoldPosition[#self.EscortMenuHoldPosition+1]=MENU_GROUP_COMMAND -:New( -self.EscortClient:GetGroup(), -MenuText, -self.EscortMenuHold, -ESCORT._HoldPosition, -self, -self.EscortGroup, -Height, -Seconds -) -end -return self -end -function ESCORT:MenuHoldAtLeaderPosition(Height,Seconds,MenuTextFormat) -self:F({Height,Seconds,MenuTextFormat}) -if self.EscortGroup:IsAir()then -if not self.EscortMenuHold then -self.EscortMenuHold=MENU_GROUP:New(self.EscortClient:GetGroup(),"Hold position",self.EscortMenu) -end -if not Height then -Height=30 -end -if not Seconds then -Seconds=0 -end -local MenuText="" -if not MenuTextFormat then -if Seconds==0 then -MenuText=string.format("Rejoin and hold at %d meter",Height) -else -MenuText=string.format("Rejoin and hold at %d meter for %d seconds",Height,Seconds) -end -else -if Seconds==0 then -MenuText=string.format(MenuTextFormat,Height) -else -MenuText=string.format(MenuTextFormat,Height,Seconds) -end -end -if not self.EscortMenuHoldAtLeaderPosition then -self.EscortMenuHoldAtLeaderPosition={} -end -self.EscortMenuHoldAtLeaderPosition[#self.EscortMenuHoldAtLeaderPosition+1]=MENU_GROUP_COMMAND -:New( -self.EscortClient:GetGroup(), -MenuText, -self.EscortMenuHold, -ESCORT._HoldPosition, -{ParamSelf=self, -ParamOrbitGroup=self.EscortClient, -ParamHeight=Height, -ParamSeconds=Seconds -} -) -end -return self -end -function ESCORT:MenuScanForTargets(Height,Seconds,MenuTextFormat) -self:F({Height,Seconds,MenuTextFormat}) -if self.EscortGroup:IsAir()then -if not self.EscortMenuScan then -self.EscortMenuScan=MENU_GROUP:New(self.EscortClient:GetGroup(),"Scan for targets",self.EscortMenu) -end -if not Height then -Height=100 -end -if not Seconds then -Seconds=30 -end -local MenuText="" -if not MenuTextFormat then -if Seconds==0 then -MenuText=string.format("At %d meter",Height) -else -MenuText=string.format("At %d meter for %d seconds",Height,Seconds) -end -else -if Seconds==0 then -MenuText=string.format(MenuTextFormat,Height) -else -MenuText=string.format(MenuTextFormat,Height,Seconds) -end -end -if not self.EscortMenuScanForTargets then -self.EscortMenuScanForTargets={} -end -self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1]=MENU_GROUP_COMMAND -:New( -self.EscortClient:GetGroup(), -MenuText, -self.EscortMenuScan, -ESCORT._ScanTargets, -self, -30 -) -end -return self -end -function ESCORT:MenuFlare(MenuTextFormat) -self:F() -if not self.EscortMenuReportNavigation then -self.EscortMenuReportNavigation=MENU_GROUP:New(self.EscortClient:GetGroup(),"Navigation",self.EscortMenu) -end -local MenuText="" -if not MenuTextFormat then -MenuText="Flare" -else -MenuText=MenuTextFormat -end -if not self.EscortMenuFlare then -self.EscortMenuFlare=MENU_GROUP:New(self.EscortClient:GetGroup(),MenuText,self.EscortMenuReportNavigation,ESCORT._Flare,self) -self.EscortMenuFlareGreen=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release green flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Green,"Released a green flare!") -self.EscortMenuFlareRed=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release red flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Red,"Released a red flare!") -self.EscortMenuFlareWhite=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release white flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.White,"Released a white flare!") -self.EscortMenuFlareYellow=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release yellow flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Yellow,"Released a yellow flare!") -end -return self -end -function ESCORT:MenuSmoke(MenuTextFormat) -self:F() -if not self.EscortGroup:IsAir()then -if not self.EscortMenuReportNavigation then -self.EscortMenuReportNavigation=MENU_GROUP:New(self.EscortClient:GetGroup(),"Navigation",self.EscortMenu) -end -local MenuText="" -if not MenuTextFormat then -MenuText="Smoke" -else -MenuText=MenuTextFormat -end -if not self.EscortMenuSmoke then -self.EscortMenuSmoke=MENU_GROUP:New(self.EscortClient:GetGroup(),"Smoke",self.EscortMenuReportNavigation,ESCORT._Smoke,self) -self.EscortMenuSmokeGreen=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release green smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Green,"Releasing green smoke!") -self.EscortMenuSmokeRed=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release red smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Red,"Releasing red smoke!") -self.EscortMenuSmokeWhite=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release white smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.White,"Releasing white smoke!") -self.EscortMenuSmokeOrange=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release orange smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Orange,"Releasing orange smoke!") -self.EscortMenuSmokeBlue=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release blue smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Blue,"Releasing blue smoke!") -end -end -return self -end -function ESCORT:MenuReportTargets(Seconds) -self:F({Seconds}) -if not self.EscortMenuReportNearbyTargets then -self.EscortMenuReportNearbyTargets=MENU_GROUP:New(self.EscortClient:GetGroup(),"Report targets",self.EscortMenu) -end -if not Seconds then -Seconds=30 -end -self.EscortMenuReportNearbyTargetsNow=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Report targets now!",self.EscortMenuReportNearbyTargets,ESCORT._ReportNearbyTargetsNow,self) -self.EscortMenuReportNearbyTargetsOn=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Report targets on",self.EscortMenuReportNearbyTargets,ESCORT._SwitchReportNearbyTargets,self,true) -self.EscortMenuReportNearbyTargetsOff=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Report targets off",self.EscortMenuReportNearbyTargets,ESCORT._SwitchReportNearbyTargets,self,false) -self.EscortMenuAttackNearbyTargets=MENU_GROUP:New(self.EscortClient:GetGroup(),"Attack targets",self.EscortMenu) -self.ReportTargetsScheduler=SCHEDULER:New(self,self._ReportTargetsScheduler,{},1,Seconds) -return self -end -function ESCORT:MenuAssistedAttack() -self:F() -self.EscortMenuTargetAssistance=MENU_GROUP:New(self.EscortClient:GetGroup(),"Request assistance from",self.EscortMenu) -return self -end -function ESCORT:MenuROE(MenuTextFormat) -self:F(MenuTextFormat) -if not self.EscortMenuROE then -self.EscortMenuROE=MENU_GROUP:New(self.EscortClient:GetGroup(),"ROE",self.EscortMenu) -if self.EscortGroup:OptionROEHoldFirePossible()then -self.EscortMenuROEHoldFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Hold Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEHoldFire(),"Holding weapons!") -end -if self.EscortGroup:OptionROEReturnFirePossible()then -self.EscortMenuROEReturnFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Return Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEReturnFire(),"Returning fire!") -end -if self.EscortGroup:OptionROEOpenFirePossible()then -self.EscortMenuROEOpenFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Open Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEOpenFire(),"Opening fire on designated targets!!") -end -if self.EscortGroup:OptionROEWeaponFreePossible()then -self.EscortMenuROEWeaponFree=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Weapon Free",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEWeaponFree(),"Opening fire on targets of opportunity!") -end -end -return self -end -function ESCORT:MenuEvasion(MenuTextFormat) -self:F(MenuTextFormat) -if self.EscortGroup:IsAir()then -if not self.EscortMenuEvasion then -self.EscortMenuEvasion=MENU_GROUP:New(self.EscortClient:GetGroup(),"Evasion",self.EscortMenu) -if self.EscortGroup:OptionROTNoReactionPossible()then -self.EscortMenuEvasionNoReaction=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Fight until death",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTNoReaction(),"Fighting until death!") -end -if self.EscortGroup:OptionROTPassiveDefensePossible()then -self.EscortMenuEvasionPassiveDefense=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Use flares, chaff and jammers",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTPassiveDefense(),"Defending using jammers, chaff and flares!") -end -if self.EscortGroup:OptionROTEvadeFirePossible()then -self.EscortMenuEvasionEvadeFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Evade enemy fire",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTEvadeFire(),"Evading on enemy fire!") -end -if self.EscortGroup:OptionROTVerticalPossible()then -self.EscortMenuOptionEvasionVertical=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Go below radar and evade fire",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTVertical(),"Evading on enemy fire with vertical manoeuvres!") -end -end -end -return self -end -function ESCORT:MenuResumeMission() -self:F() -if not self.EscortMenuResumeMission then -self.EscortMenuResumeMission=MENU_GROUP:New(self.EscortClient:GetGroup(),"Resume mission from",self.EscortMenu) -end -return self -end -function ESCORT:_HoldPosition(OrbitGroup,OrbitHeight,OrbitSeconds) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -local OrbitUnit=OrbitGroup:GetUnit(1) -self.FollowScheduler:Stop(self.FollowSchedule) -local PointFrom={} -local GroupVec3=EscortGroup:GetUnit(1):GetVec3() -PointFrom={} -PointFrom.x=GroupVec3.x -PointFrom.y=GroupVec3.z -PointFrom.speed=250 -PointFrom.type=AI.Task.WaypointType.TURNING_POINT -PointFrom.alt=GroupVec3.y -PointFrom.alt_type=AI.Task.AltitudeType.BARO -local OrbitPoint=OrbitUnit:GetVec2() -local PointTo={} -PointTo.x=OrbitPoint.x -PointTo.y=OrbitPoint.y -PointTo.speed=250 -PointTo.type=AI.Task.WaypointType.TURNING_POINT -PointTo.alt=OrbitHeight -PointTo.alt_type=AI.Task.AltitudeType.BARO -PointTo.task=EscortGroup:TaskOrbitCircleAtVec2(OrbitPoint,OrbitHeight,0) -local Points={PointFrom,PointTo} -EscortGroup:OptionROEHoldFire() -EscortGroup:OptionROTPassiveDefense() -EscortGroup:SetTask(EscortGroup:TaskRoute(Points)) -EscortGroup:MessageToClient("Orbiting at location.",10,EscortClient) -end -function ESCORT:_JoinUpAndFollow(Distance) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -self.Distance=Distance -self:JoinUpAndFollow(EscortGroup,EscortClient,self.Distance) -end -function ESCORT:JoinUpAndFollow(EscortGroup,EscortClient,Distance) -self:F({EscortGroup,EscortClient,Distance}) -self.FollowScheduler:Stop(self.FollowSchedule) -EscortGroup:OptionROEHoldFire() -EscortGroup:OptionROTPassiveDefense() -self.EscortMode=ESCORT.MODE.FOLLOW -self.CT1=0 -self.GT1=0 -self.FollowScheduler:Start(self.FollowSchedule) -EscortGroup:MessageToClient("Rejoining and Following at "..Distance.."!",30,EscortClient) -end -function ESCORT:_Flare(Color,Message) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -EscortGroup:GetUnit(1):Flare(Color) -EscortGroup:MessageToClient(Message,10,EscortClient) -end -function ESCORT:_Smoke(Color,Message) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -EscortGroup:GetUnit(1):Smoke(Color) -EscortGroup:MessageToClient(Message,10,EscortClient) -end -function ESCORT:_ReportNearbyTargetsNow() -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -self:_ReportTargetsScheduler() -end -function ESCORT:_SwitchReportNearbyTargets(ReportTargets) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -self.ReportTargets=ReportTargets -if self.ReportTargets then -if not self.ReportTargetsScheduler then -self.ReportTargetsScheduler:Schedule(self,self._ReportTargetsScheduler,{},1,30) -end -else -routines.removeFunction(self.ReportTargetsScheduler) -self.ReportTargetsScheduler=nil -end -end -function ESCORT:_ScanTargets(ScanDuration) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -self.FollowScheduler:Stop(self.FollowSchedule) -if EscortGroup:IsHelicopter()then -EscortGroup:PushTask( -EscortGroup:TaskControlled( -EscortGroup:TaskOrbitCircle(200,20), -EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil) -),1) -elseif EscortGroup:IsAirPlane()then -EscortGroup:PushTask( -EscortGroup:TaskControlled( -EscortGroup:TaskOrbitCircle(1000,500), -EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil) -),1) -end -EscortGroup:MessageToClient("Scanning targets for "..ScanDuration.." seconds.",ScanDuration,EscortClient) -if self.EscortMode==ESCORT.MODE.FOLLOW then -self.FollowScheduler:Start(self.FollowSchedule) -end -end -function _Resume(EscortGroup) -env.info('_Resume') -local Escort=EscortGroup:GetState(EscortGroup,"Escort") -env.info("EscortMode = "..Escort.EscortMode) -if Escort.EscortMode==ESCORT.MODE.FOLLOW then -Escort:JoinUpAndFollow(EscortGroup,Escort.EscortClient,Escort.Distance) -end -end -function ESCORT:_AttackTarget(DetectedItem) -local EscortGroup=self.EscortGroup -self:F(EscortGroup) -local EscortClient=self.EscortClient -self.FollowScheduler:Stop(self.FollowSchedule) -if EscortGroup:IsAir()then -EscortGroup:OptionROEOpenFire() -EscortGroup:OptionROTPassiveDefense() -EscortGroup:SetState(EscortGroup,"Escort",self) -local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem) -local Tasks={} -DetectedSet:ForEachUnit( -function(DetectedUnit,Tasks) -if DetectedUnit:IsAlive()then -Tasks[#Tasks+1]=EscortGroup:TaskAttackUnit(DetectedUnit) -end -end,Tasks -) -Tasks[#Tasks+1]=EscortGroup:TaskFunction("_Resume",{"''"}) -EscortGroup:SetTask( -EscortGroup:TaskCombo( -Tasks -),1 -) -else -local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem) -local Tasks={} -DetectedSet:ForEachUnit( -function(DetectedUnit,Tasks) -if DetectedUnit:IsAlive()then -Tasks[#Tasks+1]=EscortGroup:TaskFireAtPoint(DetectedUnit:GetVec2(),50) -end -end,Tasks -) -EscortGroup:SetTask( -EscortGroup:TaskCombo( -Tasks -),1 -) -end -EscortGroup:MessageToClient("Engaging Designated Unit!",10,EscortClient) -end -function ESCORT:_AssistTarget(EscortGroupAttack,DetectedItem) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -self.FollowScheduler:Stop(self.FollowSchedule) -if EscortGroupAttack:IsAir()then -EscortGroupAttack:OptionROEOpenFire() -EscortGroupAttack:OptionROTVertical() -local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem) -local Tasks={} -DetectedSet:ForEachUnit( -function(DetectedUnit,Tasks) -if DetectedUnit:IsAlive()then -Tasks[#Tasks+1]=EscortGroupAttack:TaskAttackUnit(DetectedUnit) -end -end,Tasks -) -Tasks[#Tasks+1]=EscortGroupAttack:TaskOrbitCircle(500,350) -EscortGroupAttack:SetTask( -EscortGroupAttack:TaskCombo( -Tasks -),1 -) -else -local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem) -local Tasks={} -DetectedSet:ForEachUnit( -function(DetectedUnit,Tasks) -if DetectedUnit:IsAlive()then -Tasks[#Tasks+1]=EscortGroupAttack:TaskFireAtPoint(DetectedUnit:GetVec2(),50) -end -end,Tasks -) -EscortGroupAttack:SetTask( -EscortGroupAttack:TaskCombo( -Tasks -),1 -) -end -EscortGroupAttack:MessageToClient("Assisting with the destroying the enemy unit!",10,EscortClient) -end -function ESCORT:_ROE(EscortROEFunction,EscortROEMessage) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -pcall(function()EscortROEFunction()end) -EscortGroup:MessageToClient(EscortROEMessage,10,EscortClient) -end -function ESCORT:_ROT(EscortROTFunction,EscortROTMessage) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -pcall(function()EscortROTFunction()end) -EscortGroup:MessageToClient(EscortROTMessage,10,EscortClient) -end -function ESCORT:_ResumeMission(WayPoint) -local EscortGroup=self.EscortGroup -local EscortClient=self.EscortClient -self.FollowScheduler:Stop(self.FollowSchedule) -local WayPoints=EscortGroup:GetTaskRoute() -self:T(WayPoint,WayPoints) -for WayPointIgnore=1,WayPoint do -table.remove(WayPoints,1) -end -SCHEDULER:New(EscortGroup,EscortGroup.SetTask,{EscortGroup:TaskRoute(WayPoints)},1) -EscortGroup:MessageToClient("Resuming mission from waypoint "..WayPoint..".",10,EscortClient) -end -function ESCORT:RegisterRoute() -self:F() -local EscortGroup=self.EscortGroup -local TaskPoints=EscortGroup:GetTaskRoute() -self:T(TaskPoints) -return TaskPoints -end -function ESCORT:_FollowScheduler() -self:F({self.FollowDistance}) -self:T({self.EscortClient.UnitName,self.EscortGroup.GroupName}) -if self.EscortGroup:IsAlive()and self.EscortClient:IsAlive()then -local ClientUnit=self.EscortClient:GetClientGroupUnit() -local GroupUnit=self.EscortGroup:GetUnit(1) -local FollowDistance=self.FollowDistance -self:T({ClientUnit.UnitName,GroupUnit.UnitName}) -if self.CT1==0 and self.GT1==0 then -self.CV1=ClientUnit:GetVec3() -self:T({"self.CV1",self.CV1}) -self.CT1=timer.getTime() -self.GV1=GroupUnit:GetVec3() -self.GT1=timer.getTime() -else -local CT1=self.CT1 -local CT2=timer.getTime() -local CV1=self.CV1 -local CV2=ClientUnit:GetVec3() -self.CT1=CT2 -self.CV1=CV2 -local CD=((CV2.x-CV1.x)^2+(CV2.y-CV1.y)^2+(CV2.z-CV1.z)^2)^0.5 -local CT=CT2-CT1 -local CS=(3600/CT)*(CD/1000) -self:T2({"Client:",CS,CD,CT,CV2,CV1,CT2,CT1}) -local GT1=self.GT1 -local GT2=timer.getTime() -local GV1=self.GV1 -local GV2=GroupUnit:GetVec3() -self.GT1=GT2 -self.GV1=GV2 -local GD=((GV2.x-GV1.x)^2+(GV2.y-GV1.y)^2+(GV2.z-GV1.z)^2)^0.5 -local GT=GT2-GT1 -local GS=(3600/GT)*(GD/1000) -self:T2({"Group:",GS,GD,GT,GV2,GV1,GT2,GT1}) -local GV={x=GV2.x-CV2.x,y=GV2.y-CV2.y,z=GV2.z-CV2.z} -local GH2={x=GV2.x,y=CV2.y,z=GV2.z} -local alpha=math.atan2(GV.z,GV.x) -local CVI={x=CV2.x+FollowDistance*math.cos(alpha), -y=GH2.y, -z=CV2.z+FollowDistance*math.sin(alpha), -} -local DV={x=CV2.x-CVI.x,y=CV2.y-CVI.y,z=CV2.z-CVI.z} -local DVu={x=DV.x/FollowDistance,y=DV.y/FollowDistance,z=DV.z/FollowDistance} -local GDV={x=DVu.x*CS*8+CVI.x,y=CVI.y,z=DVu.z*CS*8+CVI.z} -if self.SmokeDirectionVector==true then -trigger.action.smoke(GDV,trigger.smokeColor.Red) -end -self:T2({"CV2:",CV2}) -self:T2({"CVI:",CVI}) -self:T2({"GDV:",GDV}) -local CatchUpDistance=((GDV.x-GV2.x)^2+(GDV.y-GV2.y)^2+(GDV.z-GV2.z)^2)^0.5 -local Time=10 -local CatchUpSpeed=(CatchUpDistance-(CS*8.4))/Time -local Speed=CS+CatchUpSpeed -if Speed<0 then -Speed=0 -end -self:T({"Client Speed, Escort Speed, Speed, FollowDistance, Time:",CS,GS,Speed,FollowDistance,Time}) -self.EscortGroup:RouteToVec3(GDV,Speed/3.6) -end -return true -end -return false -end -function ESCORT:_ReportTargetsScheduler() -self:F(self.EscortGroup:GetName()) -if self.EscortGroup:IsAlive()and self.EscortClient:IsAlive()then -if true then -local EscortGroupName=self.EscortGroup:GetName() -self.EscortMenuAttackNearbyTargets:RemoveSubMenus() -if self.EscortMenuTargetAssistance then -self.EscortMenuTargetAssistance:RemoveSubMenus() -end -local DetectedItems=self.Detection:GetDetectedItems() -self:F(DetectedItems) -local DetectedTargets=false -local DetectedMsgs={} -for ClientEscortGroupName,EscortGroupData in pairs(self.EscortClient._EscortGroups)do -local ClientEscortTargets=EscortGroupData.Detection -for DetectedItemIndex,DetectedItem in pairs(DetectedItems)do -self:F({DetectedItemIndex,DetectedItem}) -local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,EscortGroupData.EscortGroup,_DATABASE:GetPlayerSettings(self.EscortClient:GetPlayerName())) -if ClientEscortGroupName==EscortGroupName then -local DetectedMsg=DetectedItemReportSummary:Text("\n") -DetectedMsgs[#DetectedMsgs+1]=DetectedMsg -self:T(DetectedMsg) -MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(), -DetectedMsg, -self.EscortMenuAttackNearbyTargets, -ESCORT._AttackTarget, -self, -DetectedItem -) -else -if self.EscortMenuTargetAssistance then -local DetectedMsg=DetectedItemReportSummary:Text("\n") -self:T(DetectedMsg) -local MenuTargetAssistance=MENU_GROUP:New(self.EscortClient:GetGroup(),EscortGroupData.EscortName,self.EscortMenuTargetAssistance) -MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(), -DetectedMsg, -MenuTargetAssistance, -ESCORT._AssistTarget, -self, -EscortGroupData.EscortGroup, -DetectedItem -) -end -end -DetectedTargets=true -end -end -self:F(DetectedMsgs) -if DetectedTargets then -self.EscortGroup:MessageToClient("Reporting detected targets:\n"..table.concat(DetectedMsgs,"\n"),20,self.EscortClient) -else -self.EscortGroup:MessageToClient("No targets detected.",10,self.EscortClient) -end -return true -else -end -end -return false -end -MISSILETRAINER={ -ClassName="MISSILETRAINER", -TrackingMissiles={}, -} -function MISSILETRAINER._Alive(Client,self) -if self.Briefing then -Client:Message(self.Briefing,15,"Trainer") -end -if self.MenusOnOff==true then -Client:Message("Use the 'Radio Menu' -> 'Other (F10)' -> 'Missile Trainer' menu options to change the Missile Trainer settings (for all players).",15,"Trainer") -Client.MainMenu=MENU_GROUP:New(Client:GetGroup(),"Missile Trainer",nil) -Client.MenuMessages=MENU_GROUP:New(Client:GetGroup(),"Messages",Client.MainMenu) -Client.MenuOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Messages On",Client.MenuMessages,self._MenuMessages,{MenuSelf=self,MessagesOnOff=true}) -Client.MenuOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Messages Off",Client.MenuMessages,self._MenuMessages,{MenuSelf=self,MessagesOnOff=false}) -Client.MenuTracking=MENU_GROUP:New(Client:GetGroup(),"Tracking",Client.MainMenu) -Client.MenuTrackingToAll=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To All",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingToAll=true}) -Client.MenuTrackingToTarget=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To Target",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingToAll=false}) -Client.MenuTrackOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Tracking On",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingOnOff=true}) -Client.MenuTrackOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Tracking Off",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingOnOff=false}) -Client.MenuTrackIncrease=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Frequency Increase",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingFrequency=-1}) -Client.MenuTrackDecrease=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Frequency Decrease",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingFrequency=1}) -Client.MenuAlerts=MENU_GROUP:New(Client:GetGroup(),"Alerts",Client.MainMenu) -Client.MenuAlertsToAll=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To All",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsToAll=true}) -Client.MenuAlertsToTarget=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To Target",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsToAll=false}) -Client.MenuHitsOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Hits On",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsHitsOnOff=true}) -Client.MenuHitsOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Hits Off",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsHitsOnOff=false}) -Client.MenuLaunchesOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Launches On",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsLaunchesOnOff=true}) -Client.MenuLaunchesOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Launches Off",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsLaunchesOnOff=false}) -Client.MenuDetails=MENU_GROUP:New(Client:GetGroup(),"Details",Client.MainMenu) -Client.MenuDetailsDistanceOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Range On",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsRangeOnOff=true}) -Client.MenuDetailsDistanceOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Range Off",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsRangeOnOff=false}) -Client.MenuDetailsBearingOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Bearing On",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsBearingOnOff=true}) -Client.MenuDetailsBearingOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Bearing Off",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsBearingOnOff=false}) -Client.MenuDistance=MENU_GROUP:New(Client:GetGroup(),"Set distance to plane",Client.MainMenu) -Client.MenuDistance50=MENU_GROUP_COMMAND:New(Client:GetGroup(),"50 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=50/1000}) -Client.MenuDistance100=MENU_GROUP_COMMAND:New(Client:GetGroup(),"100 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=100/1000}) -Client.MenuDistance150=MENU_GROUP_COMMAND:New(Client:GetGroup(),"150 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=150/1000}) -Client.MenuDistance200=MENU_GROUP_COMMAND:New(Client:GetGroup(),"200 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=200/1000}) -else -if Client.MainMenu then -Client.MainMenu:Remove() -end -end -local ClientID=Client:GetID() -self:T(ClientID) -if not self.TrackingMissiles[ClientID]then -self.TrackingMissiles[ClientID]={} -end -self.TrackingMissiles[ClientID].Client=Client -if not self.TrackingMissiles[ClientID].MissileData then -self.TrackingMissiles[ClientID].MissileData={} -end -end -function MISSILETRAINER:New(Distance,Briefing) -local self=BASE:Inherit(self,BASE:New()) -self:F(Distance) -if Briefing then -self.Briefing=Briefing -end -self.Schedulers={} -self.SchedulerID=0 -self.MessageInterval=2 -self.MessageLastTime=timer.getTime() -self.Distance=Distance/1000 -self:HandleEvent(EVENTS.Shot) -self.DBClients=SET_CLIENT:New():FilterStart() -self.DBClients:ForEachClient( -function(Client) -self:F("ForEach:"..Client.UnitName) -Client:Alive(self._Alive,self) -end -) -self.MessagesOnOff=true -self.TrackingToAll=false -self.TrackingOnOff=true -self.TrackingFrequency=3 -self.AlertsToAll=true -self.AlertsHitsOnOff=true -self.AlertsLaunchesOnOff=true -self.DetailsRangeOnOff=true -self.DetailsBearingOnOff=true -self.MenusOnOff=true -self.TrackingMissiles={} -self.TrackingScheduler=SCHEDULER:New(self,self._TrackMissiles,{},0.5,0.05,0) -return self -end -function MISSILETRAINER:InitMessagesOnOff(MessagesOnOff) -self:F(MessagesOnOff) -self.MessagesOnOff=MessagesOnOff -if self.MessagesOnOff==true then -MESSAGE:New("Messages ON",15,"Menu"):ToAll() -else -MESSAGE:New("Messages OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitTrackingToAll(TrackingToAll) -self:F(TrackingToAll) -self.TrackingToAll=TrackingToAll -if self.TrackingToAll==true then -MESSAGE:New("Missile tracking to all players ON",15,"Menu"):ToAll() -else -MESSAGE:New("Missile tracking to all players OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitTrackingOnOff(TrackingOnOff) -self:F(TrackingOnOff) -self.TrackingOnOff=TrackingOnOff -if self.TrackingOnOff==true then -MESSAGE:New("Missile tracking ON",15,"Menu"):ToAll() -else -MESSAGE:New("Missile tracking OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitTrackingFrequency(TrackingFrequency) -self:F(TrackingFrequency) -self.TrackingFrequency=self.TrackingFrequency+TrackingFrequency -if self.TrackingFrequency<0.5 then -self.TrackingFrequency=0.5 -end -if self.TrackingFrequency then -MESSAGE:New("Missile tracking frequency is "..self.TrackingFrequency.." seconds.",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitAlertsToAll(AlertsToAll) -self:F(AlertsToAll) -self.AlertsToAll=AlertsToAll -if self.AlertsToAll==true then -MESSAGE:New("Alerts to all players ON",15,"Menu"):ToAll() -else -MESSAGE:New("Alerts to all players OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitAlertsHitsOnOff(AlertsHitsOnOff) -self:F(AlertsHitsOnOff) -self.AlertsHitsOnOff=AlertsHitsOnOff -if self.AlertsHitsOnOff==true then -MESSAGE:New("Alerts Hits ON",15,"Menu"):ToAll() -else -MESSAGE:New("Alerts Hits OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitAlertsLaunchesOnOff(AlertsLaunchesOnOff) -self:F(AlertsLaunchesOnOff) -self.AlertsLaunchesOnOff=AlertsLaunchesOnOff -if self.AlertsLaunchesOnOff==true then -MESSAGE:New("Alerts Launches ON",15,"Menu"):ToAll() -else -MESSAGE:New("Alerts Launches OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitRangeOnOff(DetailsRangeOnOff) -self:F(DetailsRangeOnOff) -self.DetailsRangeOnOff=DetailsRangeOnOff -if self.DetailsRangeOnOff==true then -MESSAGE:New("Range display ON",15,"Menu"):ToAll() -else -MESSAGE:New("Range display OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitBearingOnOff(DetailsBearingOnOff) -self:F(DetailsBearingOnOff) -self.DetailsBearingOnOff=DetailsBearingOnOff -if self.DetailsBearingOnOff==true then -MESSAGE:New("Bearing display OFF",15,"Menu"):ToAll() -else -MESSAGE:New("Bearing display OFF",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER:InitMenusOnOff(MenusOnOff) -self:F(MenusOnOff) -self.MenusOnOff=MenusOnOff -if self.MenusOnOff==true then -MESSAGE:New("Menus are ENABLED (only when a player rejoins a slot)",15,"Menu"):ToAll() -else -MESSAGE:New("Menus are DISABLED",15,"Menu"):ToAll() -end -return self -end -function MISSILETRAINER._MenuMessages(MenuParameters) -local self=MenuParameters.MenuSelf -if MenuParameters.MessagesOnOff~=nil then -self:InitMessagesOnOff(MenuParameters.MessagesOnOff) -end -if MenuParameters.TrackingToAll~=nil then -self:InitTrackingToAll(MenuParameters.TrackingToAll) -end -if MenuParameters.TrackingOnOff~=nil then -self:InitTrackingOnOff(MenuParameters.TrackingOnOff) -end -if MenuParameters.TrackingFrequency~=nil then -self:InitTrackingFrequency(MenuParameters.TrackingFrequency) -end -if MenuParameters.AlertsToAll~=nil then -self:InitAlertsToAll(MenuParameters.AlertsToAll) -end -if MenuParameters.AlertsHitsOnOff~=nil then -self:InitAlertsHitsOnOff(MenuParameters.AlertsHitsOnOff) -end -if MenuParameters.AlertsLaunchesOnOff~=nil then -self:InitAlertsLaunchesOnOff(MenuParameters.AlertsLaunchesOnOff) -end -if MenuParameters.DetailsRangeOnOff~=nil then -self:InitRangeOnOff(MenuParameters.DetailsRangeOnOff) -end -if MenuParameters.DetailsBearingOnOff~=nil then -self:InitBearingOnOff(MenuParameters.DetailsBearingOnOff) -end -if MenuParameters.Distance~=nil then -self.Distance=MenuParameters.Distance -MESSAGE:New("Hit detection distance set to "..(self.Distance*1000).." meters",15,"Menu"):ToAll() -end -end -function MISSILETRAINER:OnEventShot(EVentData) -self:F({EVentData}) -local TrainerSourceDCSUnit=EVentData.IniDCSUnit -local TrainerSourceDCSUnitName=EVentData.IniDCSUnitName -local TrainerWeapon=EVentData.Weapon -local TrainerWeaponName=EVentData.WeaponName -self:T("Missile Launched = "..TrainerWeaponName) -local TrainerTargetDCSUnit=TrainerWeapon:getTarget() -if TrainerTargetDCSUnit then -local TrainerTargetDCSUnitName=Unit.getName(TrainerTargetDCSUnit) -local TrainerTargetSkill=_DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill -self:T(TrainerTargetDCSUnitName) -local Client=self.DBClients:FindClient(TrainerTargetDCSUnitName) -if Client then -local TrainerSourceUnit=UNIT:Find(TrainerSourceDCSUnit) -local TrainerTargetUnit=UNIT:Find(TrainerTargetDCSUnit) -if self.MessagesOnOff==true and self.AlertsLaunchesOnOff==true then -local Message=MESSAGE:New( -string.format("%s launched a %s", -TrainerSourceUnit:GetTypeName(), -TrainerWeaponName -)..self:_AddRange(Client,TrainerWeapon)..self:_AddBearing(Client,TrainerWeapon),5,"Launch Alert") -if self.AlertsToAll then -Message:ToAll() -else -Message:ToClient(Client) -end -end -local ClientID=Client:GetID() -self:T(ClientID) -local MissileData={} -MissileData.TrainerSourceUnit=TrainerSourceUnit -MissileData.TrainerWeapon=TrainerWeapon -MissileData.TrainerTargetUnit=TrainerTargetUnit -MissileData.TrainerWeaponTypeName=TrainerWeapon:getTypeName() -MissileData.TrainerWeaponLaunched=true -table.insert(self.TrackingMissiles[ClientID].MissileData,MissileData) -end -else -if(TrainerWeapon:getTypeName()=="9M311")then -SCHEDULER:New(TrainerWeapon,TrainerWeapon.destroy,{},1) -else -end -end -end -function MISSILETRAINER:_AddRange(Client,TrainerWeapon) -local RangeText="" -if self.DetailsRangeOnOff then -local PositionMissile=TrainerWeapon:getPoint() -local TargetVec3=Client:GetVec3() -local Range=((PositionMissile.x-TargetVec3.x)^2+ -(PositionMissile.y-TargetVec3.y)^2+ -(PositionMissile.z-TargetVec3.z)^2 -)^0.5/1000 -RangeText=string.format(", at %4.2fkm",Range) -end -return RangeText -end -function MISSILETRAINER:_AddBearing(Client,TrainerWeapon) -local BearingText="" -if self.DetailsBearingOnOff then -local PositionMissile=TrainerWeapon:getPoint() -local TargetVec3=Client:GetVec3() -self:T2({TargetVec3,PositionMissile}) -local DirectionVector={x=PositionMissile.x-TargetVec3.x,y=PositionMissile.y-TargetVec3.y,z=PositionMissile.z-TargetVec3.z} -local DirectionRadians=math.atan2(DirectionVector.z,DirectionVector.x) -if DirectionRadians<0 then -DirectionRadians=DirectionRadians+2*math.pi -end -local DirectionDegrees=DirectionRadians*180/math.pi -BearingText=string.format(", %d degrees",DirectionDegrees) -end -return BearingText -end -function MISSILETRAINER:_TrackMissiles() -self:F2() -local ShowMessages=false -if self.MessagesOnOff and self.MessageLastTime+self.TrackingFrequency<=timer.getTime()then -self.MessageLastTime=timer.getTime() -ShowMessages=true -end -for ClientDataID,ClientData in pairs(self.TrackingMissiles)do -local Client=ClientData.Client -if Client and Client:IsAlive()then -for MissileDataID,MissileData in pairs(ClientData.MissileData)do -self:T3(MissileDataID) -local TrainerSourceUnit=MissileData.TrainerSourceUnit -local TrainerWeapon=MissileData.TrainerWeapon -local TrainerTargetUnit=MissileData.TrainerTargetUnit -local TrainerWeaponTypeName=MissileData.TrainerWeaponTypeName -local TrainerWeaponLaunched=MissileData.TrainerWeaponLaunched -if Client and Client:IsAlive()and TrainerSourceUnit and TrainerSourceUnit:IsAlive()and TrainerWeapon and TrainerWeapon:isExist()and TrainerTargetUnit and TrainerTargetUnit:IsAlive()then -local PositionMissile=TrainerWeapon:getPosition().p -local TargetVec3=Client:GetVec3() -local Distance=((PositionMissile.x-TargetVec3.x)^2+ -(PositionMissile.y-TargetVec3.y)^2+ -(PositionMissile.z-TargetVec3.z)^2 -)^0.5/1000 -if Distance<=self.Distance then -TrainerWeapon:destroy() -if self.MessagesOnOff==true and self.AlertsHitsOnOff==true then -self:T("killed") -local Message=MESSAGE:New( -string.format("%s launched by %s killed %s", -TrainerWeapon:getTypeName(), -TrainerSourceUnit:GetTypeName(), -TrainerTargetUnit:GetPlayerName() -),15,"Hit Alert") -if self.AlertsToAll==true then -Message:ToAll() -else -Message:ToClient(Client) -end -MissileData=nil -table.remove(ClientData.MissileData,MissileDataID) -self:T(ClientData.MissileData) -end -end -else -if not(TrainerWeapon and TrainerWeapon:isExist())then -if self.MessagesOnOff==true and self.AlertsLaunchesOnOff==true then -local Message=MESSAGE:New( -string.format("%s launched by %s self destructed!", -TrainerWeaponTypeName, -TrainerSourceUnit:GetTypeName() -),5,"Tracking") -if self.AlertsToAll==true then -Message:ToAll() -else -Message:ToClient(Client) -end -end -MissileData=nil -table.remove(ClientData.MissileData,MissileDataID) -self:T(ClientData.MissileData) -end -end -end -else -self.TrackingMissiles[ClientDataID]=nil -end -end -if ShowMessages==true and self.MessagesOnOff==true and self.TrackingOnOff==true then -for ClientDataID,ClientData in pairs(self.TrackingMissiles)do -local Client=ClientData.Client -ClientData.MessageToClient="" -ClientData.MessageToAll="" -for TrackingDataID,TrackingData in pairs(self.TrackingMissiles)do -for MissileDataID,MissileData in pairs(TrackingData.MissileData)do -local TrainerSourceUnit=MissileData.TrainerSourceUnit -local TrainerWeapon=MissileData.TrainerWeapon -local TrainerTargetUnit=MissileData.TrainerTargetUnit -local TrainerWeaponTypeName=MissileData.TrainerWeaponTypeName -local TrainerWeaponLaunched=MissileData.TrainerWeaponLaunched -if Client and Client:IsAlive()and TrainerSourceUnit and TrainerSourceUnit:IsAlive()and TrainerWeapon and TrainerWeapon:isExist()and TrainerTargetUnit and TrainerTargetUnit:IsAlive()then -if ShowMessages==true then -local TrackingTo -TrackingTo=string.format(" -> %s", -TrainerWeaponTypeName -) -if ClientDataID==TrackingDataID then -if ClientData.MessageToClient==""then -ClientData.MessageToClient="Missiles to You:\n" -end -ClientData.MessageToClient=ClientData.MessageToClient..TrackingTo..self:_AddRange(ClientData.Client,TrainerWeapon)..self:_AddBearing(ClientData.Client,TrainerWeapon).."\n" -else -if self.TrackingToAll==true then -if ClientData.MessageToAll==""then -ClientData.MessageToAll="Missiles to other Players:\n" -end -ClientData.MessageToAll=ClientData.MessageToAll..TrackingTo..self:_AddRange(ClientData.Client,TrainerWeapon)..self:_AddBearing(ClientData.Client,TrainerWeapon).." ( "..TrainerTargetUnit:GetPlayerName().." )\n" -end -end -end -end -end -end -if ClientData.MessageToClient~=""or ClientData.MessageToAll~=""then -local Message=MESSAGE:New(ClientData.MessageToClient..ClientData.MessageToAll,1,"Tracking"):ToClient(Client) -end -end -end -return true -end -ATC_GROUND={ -ClassName="ATC_GROUND", -SetClient=nil, -Airbases=nil, -AirbaseNames=nil, -} -function ATC_GROUND:New(Airbases,AirbaseList) -local self=BASE:Inherit(self,BASE:New()) -self:E({self.ClassName,Airbases}) -self.Airbases=Airbases -self.AirbaseList=AirbaseList -self.SetClient=SET_CLIENT:New():FilterCategories("plane"):FilterStart() -for AirbaseID,Airbase in pairs(self.Airbases)do -Airbase.ZoneBoundary=_DATABASE:FindAirbase(AirbaseID):GetZone() -Airbase.ZoneRunways={} -for PointsRunwayID,PointsRunway in pairs(Airbase.PointsRunways)do -Airbase.ZoneRunways[PointsRunwayID]=ZONE_POLYGON_BASE:New("Runway "..PointsRunwayID,PointsRunway) -end -Airbase.Monitor=self.AirbaseList and false or true -end -for AirbaseID,AirbaseName in pairs(self.AirbaseList or{})do -self.Airbases[AirbaseName].Monitor=true -end -self.SetClient:ForEachClient( -function(Client) -Client:SetState(self,"Speeding",false) -Client:SetState(self,"Warnings",0) -Client:SetState(self,"IsOffRunway",false) -Client:SetState(self,"OffRunwayWarnings",0) -Client:SetState(self,"Taxi",false) -end -) -SSB=USERFLAG:New("SSB") -SSB:Set(100) -return self -end -function ATC_GROUND:SmokeRunways(SmokeColor) -for AirbaseID,Airbase in pairs(self.Airbases)do -for PointsRunwayID,PointsRunway in pairs(Airbase.PointsRunways)do -Airbase.ZoneRunways[PointsRunwayID]:SmokeZone(SmokeColor) -end -end -end -function ATC_GROUND:SetKickSpeed(KickSpeed,Airbase) -if not Airbase then -self.KickSpeed=KickSpeed -else -self.Airbases[Airbase].KickSpeed=KickSpeed -end -return self -end -function ATC_GROUND:SetKickSpeedKmph(KickSpeed,Airbase) -self:SetKickSpeed(UTILS.KmphToMps(KickSpeed),Airbase) -return self -end -function ATC_GROUND:SetKickSpeedMiph(KickSpeedMiph,Airbase) -self:SetKickSpeed(UTILS.MiphToMps(KickSpeedMiph),Airbase) -return self -end -function ATC_GROUND:SetMaximumKickSpeed(MaximumKickSpeed,Airbase) -if not Airbase then -self.MaximumKickSpeed=MaximumKickSpeed -else -self.Airbases[Airbase].MaximumKickSpeed=MaximumKickSpeed -end -return self -end -function ATC_GROUND:SetMaximumKickSpeedKmph(MaximumKickSpeed,Airbase) -self:SetMaximumKickSpeed(UTILS.KmphToMps(MaximumKickSpeed),Airbase) -return self -end -function ATC_GROUND:SetMaximumKickSpeedMiph(MaximumKickSpeedMiph,Airbase) -self:SetMaximumKickSpeed(UTILS.MiphToMps(MaximumKickSpeedMiph),Airbase) -return self -end -function ATC_GROUND:_AirbaseMonitor() -self.SetClient:ForEachClient( -function(Client) -if Client:IsAlive()then -local IsOnGround=Client:InAir()==false -for AirbaseID,AirbaseMeta in pairs(self.Airbases)do -self:E(AirbaseID,AirbaseMeta.KickSpeed) -if AirbaseMeta.Monitor==true and Client:IsInZone(AirbaseMeta.ZoneBoundary)then -local NotInRunwayZone=true -for ZoneRunwayID,ZoneRunway in pairs(AirbaseMeta.ZoneRunways)do -NotInRunwayZone=(Client:IsNotInZone(ZoneRunway)==true)and NotInRunwayZone or false -end -if NotInRunwayZone then -if IsOnGround then -local Taxi=Client:GetState(self,"Taxi") -self:E(Taxi) -if Taxi==false then -local Velocity=VELOCITY:New(AirbaseMeta.KickSpeed or self.KickSpeed) -Client:Message("Welcome to "..AirbaseID..". The maximum taxiing speed is ".. -Velocity:ToString(),20,"ATC") -Client:SetState(self,"Taxi",true) -end -local Velocity=VELOCITY_POSITIONABLE:New(Client) -local IsAboveRunway=Client:IsAboveRunway() -self:T(IsAboveRunway,IsOnGround) -if IsOnGround then -local Speeding=false -if AirbaseMeta.MaximumKickSpeed then -if Velocity:Get()>AirbaseMeta.MaximumKickSpeed then -Speeding=true -end -else -if Velocity:Get()>self.MaximumKickSpeed then -Speeding=true -end -end -if Speeding==true then -MESSAGE:New("Penalty! Player "..Client:GetPlayerName().. -" has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll() -Client:Destroy() -Client:SetState(self,"Speeding",false) -Client:SetState(self,"Warnings",0) -end -end -if IsOnGround then -local Speeding=false -if AirbaseMeta.KickSpeed then -if Velocity:Get()>AirbaseMeta.KickSpeed then -Speeding=true -end -else -if Velocity:Get()>self.KickSpeed then -Speeding=true -end -end -if Speeding==true then -local IsSpeeding=Client:GetState(self,"Speeding") -if IsSpeeding==true then -local SpeedingWarnings=Client:GetState(self,"Warnings") -self:T(SpeedingWarnings) -if SpeedingWarnings<=3 then -Client:Message("Warning "..SpeedingWarnings.."/3! Airbase traffic rule violation! Slow down now! Your speed is ".. -Velocity:ToString(),5,"ATC") -Client:SetState(self,"Warnings",SpeedingWarnings+1) -else -MESSAGE:New("Penalty! Player "..Client:GetPlayerName().." has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll() -Client:Destroy() -Client:SetState(self,"Speeding",false) -Client:SetState(self,"Warnings",0) -end -else -Client:Message("Attention! You are speeding on the taxiway, slow down! Your speed is ".. -Velocity:ToString(),5,"ATC") -Client:SetState(self,"Speeding",true) -Client:SetState(self,"Warnings",1) -end -else -Client:SetState(self,"Speeding",false) -Client:SetState(self,"Warnings",0) -end -end -if IsOnGround and not IsAboveRunway then -local IsOffRunway=Client:GetState(self,"IsOffRunway") -if IsOffRunway==true then -local OffRunwayWarnings=Client:GetState(self,"OffRunwayWarnings") -self:T(OffRunwayWarnings) -if OffRunwayWarnings<=3 then -Client:Message("Warning "..OffRunwayWarnings.."/3! Airbase traffic rule violation! Get back on the taxi immediately!",5,"ATC") -Client:SetState(self,"OffRunwayWarnings",OffRunwayWarnings+1) -else -MESSAGE:New("Penalty! Player "..Client:GetPlayerName().." has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll() -Client:Destroy() -Client:SetState(self,"IsOffRunway",false) -Client:SetState(self,"OffRunwayWarnings",0) -end -else -Client:Message("Attention! You are off the taxiway. Get back on the taxiway immediately!",5,"ATC") -Client:SetState(self,"IsOffRunway",true) -Client:SetState(self,"OffRunwayWarnings",1) -end -else -Client:SetState(self,"IsOffRunway",false) -Client:SetState(self,"OffRunwayWarnings",0) -end -end -else -Client:SetState(self,"Speeding",false) -Client:SetState(self,"Warnings",0) -Client:SetState(self,"IsOffRunway",false) -Client:SetState(self,"OffRunwayWarnings",0) -local Taxi=Client:GetState(self,"Taxi") -if Taxi==true then -Client:Message("You have progressed to the runway ... Await take-off clearance ...",20,"ATC") -Client:SetState(self,"Taxi",false) -end -end -end -end -else -Client:SetState(self,"Taxi",false) -end -end -) -return true -end -ATC_GROUND_CAUCASUS={ -ClassName="ATC_GROUND_CAUCASUS", -Airbases={ -[AIRBASE.Caucasus.Anapa_Vityazevo]={ -PointsRunways={ -[1]={ -[1]={["y"]=242140.57142858,["x"]=-6478.8571428583,}, -[2]={["y"]=242188.57142858,["x"]=-6522.0000000011,}, -[3]={["y"]=244124.2857143,["x"]=-4344.0000000011,}, -[4]={["y"]=244068.2857143,["x"]=-4296.5714285726,}, -[5]={["y"]=242140.57142858,["x"]=-6480.0000000011,} -}, -}, -}, -[AIRBASE.Caucasus.Batumi]={ -PointsRunways={ -[1]={ -[1]={["y"]=616442.28571429,["x"]=-355090.28571429,}, -[2]={["y"]=618450.57142857,["x"]=-356522,}, -[3]={["y"]=618407.71428571,["x"]=-356584.85714286,}, -[4]={["y"]=618361.99999999,["x"]=-356554.85714286,}, -[5]={["y"]=618324.85714285,["x"]=-356599.14285715,}, -[6]={["y"]=618250.57142856,["x"]=-356543.42857143,}, -[7]={["y"]=618257.7142857,["x"]=-356496.28571429,}, -[8]={["y"]=618237.7142857,["x"]=-356459.14285715,}, -[9]={["y"]=616555.71428571,["x"]=-355258.85714286,}, -[10]={["y"]=616486.28571428,["x"]=-355280.57142858,}, -[11]={["y"]=616410.57142856,["x"]=-355227.71428572,}, -[12]={["y"]=616441.99999999,["x"]=-355179.14285715,}, -[13]={["y"]=616401.99999999,["x"]=-355147.71428572,}, -[14]={["y"]=616441.42857142,["x"]=-355092.57142858,}, -}, -}, -}, -[AIRBASE.Caucasus.Beslan]={ -PointsRunways={ -[1]={ -[1]={["y"]=842104.57142857,["x"]=-148460.57142857,}, -[2]={["y"]=845225.71428572,["x"]=-148656,}, -[3]={["y"]=845220.57142858,["x"]=-148750,}, -[4]={["y"]=842098.85714286,["x"]=-148556.28571429,}, -[5]={["y"]=842104,["x"]=-148460.28571429,}, -}, -}, -}, -[AIRBASE.Caucasus.Gelendzhik]={ -PointsRunways={ -[1]={ -[1]={["y"]=297834.00000001,["x"]=-51107.428571429,}, -[2]={["y"]=297786.57142858,["x"]=-51068.857142858,}, -[3]={["y"]=298946.57142858,["x"]=-49686.000000001,}, -[4]={["y"]=298993.14285715,["x"]=-49725.714285715,}, -[5]={["y"]=297835.14285715,["x"]=-51107.714285715,}, -}, -}, -}, -[AIRBASE.Caucasus.Gudauta]={ -PointsRunways={ -[1]={ -[1]={["y"]=517096.57142857,["x"]=-197804.57142857,}, -[2]={["y"]=515880.85714285,["x"]=-195590.28571429,}, -[3]={["y"]=515812.28571428,["x"]=-195628.85714286,}, -[4]={["y"]=517036.57142857,["x"]=-197834.57142857,}, -[5]={["y"]=517097.99999999,["x"]=-197807.42857143,}, -}, -}, -}, -[AIRBASE.Caucasus.Kobuleti]={ -PointsRunways={ -[1]={ -[1]={["y"]=634509.71428571,["x"]=-318339.42857144,}, -[2]={["y"]=636767.42857143,["x"]=-317516.57142858,}, -[3]={["y"]=636790,["x"]=-317575.71428572,}, -[4]={["y"]=634531.42857143,["x"]=-318398.00000001,}, -[5]={["y"]=634510.28571429,["x"]=-318339.71428572,}, -}, -}, -}, -[AIRBASE.Caucasus.Krasnodar_Center]={ -PointsRunways={ -[1]={ -[1]={["y"]=369205.42857144,["x"]=11789.142857142,}, -[2]={["y"]=369209.71428572,["x"]=11714.857142856,}, -[3]={["y"]=366699.71428572,["x"]=11581.714285713,}, -[4]={["y"]=366698.28571429,["x"]=11659.142857142,}, -[5]={["y"]=369208.85714286,["x"]=11788.57142857,}, -}, -}, -}, -[AIRBASE.Caucasus.Krasnodar_Pashkovsky]={ -PointsRunways={ -[1]={ -[1]={["y"]=385891.14285715,["x"]=8416.5714285703,}, -[2]={["y"]=385842.28571429,["x"]=8467.9999999989,}, -[3]={["y"]=384180.85714286,["x"]=6917.1428571417,}, -[4]={["y"]=384228.57142858,["x"]=6867.7142857132,}, -[5]={["y"]=385891.14285715,["x"]=8416.5714285703,}, -}, -[2]={ -[1]={["y"]=386714.85714286,["x"]=6674.857142856,}, -[2]={["y"]=386757.71428572,["x"]=6627.7142857132,}, -[3]={["y"]=389028.57142858,["x"]=8741.4285714275,}, -[4]={["y"]=388981.71428572,["x"]=8790.5714285703,}, -[5]={["y"]=386714.57142858,["x"]=6674.5714285703,}, -}, -}, -}, -[AIRBASE.Caucasus.Krymsk]={ -PointsRunways={ -[1]={ -[1]={["y"]=293522.00000001,["x"]=-7567.4285714297,}, -[2]={["y"]=293578.57142858,["x"]=-7616.0000000011,}, -[3]={["y"]=295246.00000001,["x"]=-5591.142857144,}, -[4]={["y"]=295187.71428573,["x"]=-5546.0000000011,}, -[5]={["y"]=293523.14285715,["x"]=-7568.2857142868,}, -}, -}, -}, -[AIRBASE.Caucasus.Kutaisi]={ -PointsRunways={ -[1]={ -[1]={["y"]=682638,["x"]=-285202.28571429,}, -[2]={["y"]=685050.28571429,["x"]=-284507.42857144,}, -[3]={["y"]=685068.85714286,["x"]=-284578.85714286,}, -[4]={["y"]=682657.42857143,["x"]=-285264.28571429,}, -[5]={["y"]=682638.28571429,["x"]=-285202.85714286,}, -}, -}, -}, -[AIRBASE.Caucasus.Maykop_Khanskaya]={ -PointsRunways={ -[1]={ -[1]={["y"]=457005.42857143,["x"]=-27668.000000001,}, -[2]={["y"]=459028.85714286,["x"]=-25168.857142858,}, -[3]={["y"]=459082.57142857,["x"]=-25216.857142858,}, -[4]={["y"]=457060,["x"]=-27714.285714287,}, -[5]={["y"]=457004.57142857,["x"]=-27669.714285715,}, -}, -}, -}, -[AIRBASE.Caucasus.Mineralnye_Vody]={ -PointsRunways={ -[1]={ -[1]={["y"]=703904,["x"]=-50352.571428573,}, -[2]={["y"]=707596.28571429,["x"]=-52094.571428573,}, -[3]={["y"]=707560.57142858,["x"]=-52161.714285716,}, -[4]={["y"]=703871.71428572,["x"]=-50420.571428573,}, -[5]={["y"]=703902,["x"]=-50352.000000002,}, -}, -}, -}, -[AIRBASE.Caucasus.Mozdok]={ -PointsRunways={ -[1]={ -[1]={["y"]=832201.14285715,["x"]=-83699.428571431,}, -[2]={["y"]=832212.57142857,["x"]=-83780.571428574,}, -[3]={["y"]=835730.28571429,["x"]=-83335.714285717,}, -[4]={["y"]=835718.85714286,["x"]=-83246.571428574,}, -[5]={["y"]=832200.57142857,["x"]=-83700.000000002,}, -}, -}, -}, -[AIRBASE.Caucasus.Nalchik]={ -PointsRunways={ -[1]={ -[1]={["y"]=759454.28571429,["x"]=-125551.42857143,}, -[2]={["y"]=759492.85714286,["x"]=-125610.85714286,}, -[3]={["y"]=761406.28571429,["x"]=-124304.28571429,}, -[4]={["y"]=761361.14285714,["x"]=-124239.71428572,}, -[5]={["y"]=759456,["x"]=-125552.57142857,}, -}, -}, -}, -[AIRBASE.Caucasus.Novorossiysk]={ -PointsRunways={ -[1]={ -[1]={["y"]=278673.14285716,["x"]=-41615.142857144,}, -[2]={["y"]=278625.42857144,["x"]=-41570.571428572,}, -[3]={["y"]=279835.42857144,["x"]=-40226.000000001,}, -[4]={["y"]=279882.2857143,["x"]=-40270.000000001,}, -[5]={["y"]=278672.00000001,["x"]=-41614.857142858,}, -}, -}, -}, -[AIRBASE.Caucasus.Senaki_Kolkhi]={ -PointsRunways={ -[1]={ -[1]={["y"]=646060.85714285,["x"]=-281736,}, -[2]={["y"]=646056.57142857,["x"]=-281631.71428571,}, -[3]={["y"]=648442.28571428,["x"]=-281840.28571428,}, -[4]={["y"]=648432.28571428,["x"]=-281918.85714286,}, -[5]={["y"]=646063.71428571,["x"]=-281738.85714286,}, -}, -}, -}, -[AIRBASE.Caucasus.Sochi_Adler]={ -PointsRunways={ -[1]={ -[1]={["y"]=460831.42857143,["x"]=-165180,}, -[2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, -[3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, -[4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, -[5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, -}, -[2]={ -[1]={["y"]=460831.42857143,["x"]=-165180,}, -[2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, -[3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, -[4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, -[5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, -}, -}, -}, -[AIRBASE.Caucasus.Soganlug]={ -PointsRunways={ -[1]={ -[1]={["y"]=894525.71428571,["x"]=-316964,}, -[2]={["y"]=896363.14285714,["x"]=-318634.28571428,}, -[3]={["y"]=896299.14285714,["x"]=-318702.85714286,}, -[4]={["y"]=894464,["x"]=-317031.71428571,}, -[5]={["y"]=894524.57142857,["x"]=-316963.71428571,}, -}, -}, -}, -[AIRBASE.Caucasus.Sukhumi_Babushara]={ -PointsRunways={ -[1]={ -[1]={["y"]=562684,["x"]=-219779.71428571,}, -[2]={["y"]=562717.71428571,["x"]=-219718,}, -[3]={["y"]=566046.85714286,["x"]=-221376.57142857,}, -[4]={["y"]=566012.28571428,["x"]=-221446.57142857,}, -[5]={["y"]=562684.57142857,["x"]=-219782.57142857,}, -}, -}, -}, -[AIRBASE.Caucasus.Tbilisi_Lochini]={ -PointsRunways={ -[1]={ -[1]={["y"]=895261.14285715,["x"]=-314652.28571428,}, -[2]={["y"]=897654.57142857,["x"]=-316523.14285714,}, -[3]={["y"]=897711.71428571,["x"]=-316450.28571429,}, -[4]={["y"]=895327.42857143,["x"]=-314568.85714286,}, -[5]={["y"]=895261.71428572,["x"]=-314656,}, -}, -[2]={ -[1]={["y"]=895605.71428572,["x"]=-314724.57142857,}, -[2]={["y"]=897639.71428572,["x"]=-316148,}, -[3]={["y"]=897683.42857143,["x"]=-316087.14285714,}, -[4]={["y"]=895650,["x"]=-314660,}, -[5]={["y"]=895606,["x"]=-314724.85714286,} -}, -}, -}, -[AIRBASE.Caucasus.Vaziani]={ -PointsRunways={ -[1]={ -[1]={["y"]=902239.14285714,["x"]=-318190.85714286,}, -[2]={["y"]=904014.28571428,["x"]=-319994.57142857,}, -[3]={["y"]=904064.85714285,["x"]=-319945.14285715,}, -[4]={["y"]=902294.57142857,["x"]=-318146,}, -[5]={["y"]=902247.71428571,["x"]=-318190.85714286,}, -}, -}, -}, -}, -} -function ATC_GROUND_CAUCASUS:New(AirbaseNames) -local self=BASE:Inherit(self,ATC_GROUND:New(self.Airbases,AirbaseNames)) -self:SetKickSpeedKmph(50) -self:SetMaximumKickSpeedKmph(150) -return self -end -function ATC_GROUND_CAUCASUS:Start(RepeatScanSeconds) -RepeatScanSeconds=RepeatScanSeconds or 0.05 -self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds) -end -ATC_GROUND_NEVADA={ -ClassName="ATC_GROUND_NEVADA", -Airbases={ -[AIRBASE.Nevada.Beatty_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-174950.05857143,["x"]=-329679.65,}, -[2]={["y"]=-174946.53828571,["x"]=-331394.03885715,}, -[3]={["y"]=-174967.10971429,["x"]=-331394.32457143,}, -[4]={["y"]=-174971.01828571,["x"]=-329682.59171429,}, -}, -}, -}, -[AIRBASE.Nevada.Boulder_City_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-1317.841714286,["x"]=-429014.92857142,}, -[2]={["y"]=-951.26228571458,["x"]=-430310.21142856,}, -[3]={["y"]=-978.11942857172,["x"]=-430317.06857142,}, -[4]={["y"]=-1347.5088571432,["x"]=-429023.98485713,}, -}, -[2]={ -[1]={["y"]=-1879.955714286,["x"]=-429783.83742856,}, -[2]={["y"]=-256.25257142886,["x"]=-430023.63542856,}, -[3]={["y"]=-260.25257142886,["x"]=-430048.77828571,}, -[4]={["y"]=-1883.955714286,["x"]=-429807.83742856,}, -}, -}, -}, -[AIRBASE.Nevada.Creech_AFB]={ -PointsRunways={ -[1]={ -[1]={["y"]=-74234.729142857,["x"]=-360501.80857143,}, -[2]={["y"]=-77606.122285714,["x"]=-360417.86542857,}, -[3]={["y"]=-77608.578,["x"]=-360486.13428571,}, -[4]={["y"]=-74237.930571428,["x"]=-360586.25628571,}, -}, -[2]={ -[1]={["y"]=-75807.571428572,["x"]=-359073.42857142,}, -[2]={["y"]=-74770.142857144,["x"]=-360581.71428571,}, -[3]={["y"]=-74641.285714287,["x"]=-360585.42857142,}, -[4]={["y"]=-75734.142857144,["x"]=-359023.14285714,}, -}, -}, -}, -[AIRBASE.Nevada.Echo_Bay]={ -PointsRunways={ -[1]={ -[1]={["y"]=33182.919428572,["x"]=-388698.21657142,}, -[2]={["y"]=34202.543142857,["x"]=-388469.55485714,}, -[3]={["y"]=34207.686,["x"]=-388488.69771428,}, -[4]={["y"]=33185.422285715,["x"]=-388717.82228571,}, -}, -}, -}, -[AIRBASE.Nevada.Groom_Lake_AFB]={ -PointsRunways={ -[1]={ -[1]={["y"]=-85971.465428571,["x"]=-290567.77,}, -[2]={["y"]=-87691.155428571,["x"]=-286637.75428571,}, -[3]={["y"]=-87756.714285715,["x"]=-286663.99999999,}, -[4]={["y"]=-86035.940285714,["x"]=-290598.81314286,}, -}, -[2]={ -[1]={["y"]=-86741.547142857,["x"]=-290353.31971428,}, -[2]={["y"]=-89672.714285714,["x"]=-283546.57142855,}, -[3]={["y"]=-89772.142857143,["x"]=-283587.71428569,}, -[4]={["y"]=-86799.623714285,["x"]=-290374.16771428,}, -}, -}, -}, -[AIRBASE.Nevada.Henderson_Executive_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-25837.500571429,["x"]=-426404.25257142,}, -[2]={["y"]=-25843.509428571,["x"]=-428752.67942856,}, -[3]={["y"]=-25902.343714286,["x"]=-428749.96399999,}, -[4]={["y"]=-25934.667142857,["x"]=-426411.45657142,}, -}, -[2]={ -[1]={["y"]=-25650.296285714,["x"]=-426510.17971428,}, -[2]={["y"]=-25632.443428571,["x"]=-428297.11428571,}, -[3]={["y"]=-25686.690285714,["x"]=-428299.37457142,}, -[4]={["y"]=-25708.296285714,["x"]=-426515.15114285,}, -}, -}, -}, -[AIRBASE.Nevada.Jean_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-42549.187142857,["x"]=-449663.23257143,}, -[2]={["y"]=-43367.466285714,["x"]=-451044.77657143,}, -[3]={["y"]=-43395.180571429,["x"]=-451028.20514286,}, -[4]={["y"]=-42579.893142857,["x"]=-449648.18371428,}, -}, -[2]={ -[1]={["y"]=-42588.359428572,["x"]=-449900.14342857,}, -[2]={["y"]=-43349.698285714,["x"]=-451185.46857143,}, -[3]={["y"]=-43369.624571429,["x"]=-451173.49342857,}, -[4]={["y"]=-42609.216571429,["x"]=-449891.28628571,}, -}, -}, -}, -[AIRBASE.Nevada.Laughlin_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=28231.600857143,["x"]=-515555.94114286,}, -[2]={["y"]=28453.728285714,["x"]=-518170.78885714,}, -[3]={["y"]=28370.788285714,["x"]=-518176.25742857,}, -[4]={["y"]=28138.022857143,["x"]=-515573.07514286,}, -}, -[2]={ -[1]={["y"]=28231.600857143,["x"]=-515555.94114286,}, -[2]={["y"]=28453.728285714,["x"]=-518170.78885714,}, -[3]={["y"]=28370.788285714,["x"]=-518176.25742857,}, -[4]={["y"]=28138.022857143,["x"]=-515573.07514286,}, -}, -}, -}, -[AIRBASE.Nevada.Lincoln_County]={ -PointsRunways={ -[1]={ -[1]={["y"]=33222.34171429,["x"]=-223959.40171429,}, -[2]={["y"]=33200.040000004,["x"]=-225369.36828572,}, -[3]={["y"]=33177.634571428,["x"]=-225369.21485715,}, -[4]={["y"]=33201.198857147,["x"]=-223960.54457143,}, -}, -}, -}, -[AIRBASE.Nevada.McCarran_International_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-29406.035714286,["x"]=-416102.48199999,}, -[2]={["y"]=-24680.714285715,["x"]=-416003.14285713,}, -[3]={["y"]=-24681.857142858,["x"]=-415926.57142856,}, -[4]={["y"]=-29408.42857143,["x"]=-416016.57142856,}, -}, -[2]={ -[1]={["y"]=-28567.221714286,["x"]=-416378.61799999,}, -[2]={["y"]=-25109.912285714,["x"]=-416309.92914285,}, -[3]={["y"]=-25112.508,["x"]=-416240.78714285,}, -[4]={["y"]=-28576.247428571,["x"]=-416308.49514285,}, -}, -[3]={ -[1]={["y"]=-29255.953142857,["x"]=-416307.10657142,}, -[2]={["y"]=-28005.571428572,["x"]=-413449.7142857,}, -[3]={["y"]=-28068.714285715,["x"]=-413422.85714284,}, -[4]={["y"]=-29331.000000001,["x"]=-416275.7142857,}, -}, -[4]={ -[1]={["y"]=-28994.901714286,["x"]=-416423.0522857,}, -[2]={["y"]=-27697.571428572,["x"]=-413464.57142856,}, -[3]={["y"]=-27767.857142858,["x"]=-413434.28571427,}, -[4]={["y"]=-29073.000000001,["x"]=-416386.85714284,}, -}, -}, -}, -[AIRBASE.Nevada.Mesquite]={ -PointsRunways={ -[1]={ -[1]={["y"]=68188.340285714,["x"]=-330302.54742857,}, -[2]={["y"]=68911.303428571,["x"]=-328920.76571429,}, -[3]={["y"]=68936.927142857,["x"]=-328933.888,}, -[4]={["y"]=68212.460285714,["x"]=-330317.19171429,}, -}, -}, -}, -[AIRBASE.Nevada.Mina_Airport_3Q0]={ -PointsRunways={ -[1]={ -[1]={["y"]=-290054.57371429,["x"]=-160930.02228572,}, -[2]={["y"]=-289469.77457143,["x"]=-162048.73571429,}, -[3]={["y"]=-289520.06028572,["x"]=-162074.73571429,}, -[4]={["y"]=-290104.69085714,["x"]=-160956.19457143,}, -}, -}, -}, -[AIRBASE.Nevada.Nellis_AFB]={ -PointsRunways={ -[1]={ -[1]={["y"]=-18614.218571428,["x"]=-399437.91085714,}, -[2]={["y"]=-16217.857142857,["x"]=-396596.85714286,}, -[3]={["y"]=-16300.142857143,["x"]=-396530,}, -[4]={["y"]=-18692.543428571,["x"]=-399381.31114286,}, -}, -[2]={ -[1]={["y"]=-18388.948857143,["x"]=-399630.51828571,}, -[2]={["y"]=-16011,["x"]=-396806.85714286,}, -[3]={["y"]=-16074.714285714,["x"]=-396751.71428572,}, -[4]={["y"]=-18451.571428572,["x"]=-399580.85714285,}, -}, -}, -}, -[AIRBASE.Nevada.Pahute_Mesa_Airstrip]={ -PointsRunways={ -[1]={ -[1]={["y"]=-132690.40942857,["x"]=-302733.53085714,}, -[2]={["y"]=-133112.43228571,["x"]=-304499.70742857,}, -[3]={["y"]=-133179.91685714,["x"]=-304485.544,}, -[4]={["y"]=-132759.988,["x"]=-302723.326,}, -}, -}, -}, -[AIRBASE.Nevada.Tonopah_Test_Range_Airfield]={ -PointsRunways={ -[1]={ -[1]={["y"]=-175389.162,["x"]=-224778.07685715,}, -[2]={["y"]=-173942.15485714,["x"]=-228210.27571429,}, -[3]={["y"]=-174001.77085714,["x"]=-228233.60371429,}, -[4]={["y"]=-175452.38685714,["x"]=-224806.84200001,}, -}, -}, -}, -[AIRBASE.Nevada.Tonopah_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-202128.25228571,["x"]=-196701.34314286,}, -[2]={["y"]=-201562.40828571,["x"]=-198814.99714286,}, -[3]={["y"]=-201591.44828571,["x"]=-198820.93714286,}, -[4]={["y"]=-202156.06828571,["x"]=-196707.68714286,}, -}, -[2]={ -[1]={["y"]=-202084.57171428,["x"]=-196722.02228572,}, -[2]={["y"]=-200592.75485714,["x"]=-197768.05571429,}, -[3]={["y"]=-200605.37285714,["x"]=-197783.49228572,}, -[4]={["y"]=-202097.14314285,["x"]=-196739.16514286,}, -}, -}, -}, -[AIRBASE.Nevada.North_Las_Vegas]={ -PointsRunways={ -[1]={ -[1]={["y"]=-32599.017714286,["x"]=-400913.26485714,}, -[2]={["y"]=-30881.068857143,["x"]=-400837.94628571,}, -[3]={["y"]=-30879.354571428,["x"]=-400873.08914285,}, -[4]={["y"]=-32595.966285714,["x"]=-400947.13571428,}, -}, -[2]={ -[1]={["y"]=-32499.448571428,["x"]=-400690.99514285,}, -[2]={["y"]=-31247.514857143,["x"]=-401868.95571428,}, -[3]={["y"]=-31271.802857143,["x"]=-401894.97857142,}, -[4]={["y"]=-32520.02,["x"]=-400716.99514285,}, -}, -[3]={ -[1]={["y"]=-31865.254857143,["x"]=-400999.74057143,}, -[2]={["y"]=-30893.604,["x"]=-401908.85742857,}, -[3]={["y"]=-30915.578857143,["x"]=-401936.03685714,}, -[4]={["y"]=-31884.969142858,["x"]=-401020.59771429,}, -}, -}, -}, -}, -} -function ATC_GROUND_NEVADA:New(AirbaseNames) -local self=BASE:Inherit(self,ATC_GROUND:New(self.Airbases,AirbaseNames)) -self:SetKickSpeedKmph(50) -self:SetMaximumKickSpeedKmph(150) -return self -end -function ATC_GROUND_NEVADA:Start(RepeatScanSeconds) -RepeatScanSeconds=RepeatScanSeconds or 0.05 -self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds) -end -ATC_GROUND_NORMANDY={ -ClassName="ATC_GROUND_NORMANDY", -Airbases={ -[AIRBASE.Normandy.Azeville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-74194.387714285,["x"]=-2691.1399999998,}, -[2]={["y"]=-73160.282571428,["x"]=-2310.0274285712,}, -[3]={["y"]=-73141.711142857,["x"]=-2357.7417142855,}, -[4]={["y"]=-74176.959142857,["x"]=-2741.997142857,}, -}, -}, -}, -[AIRBASE.Normandy.Bazenville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-19246.209999999,["x"]=-21246.748,}, -[2]={["y"]=-17883.70142857,["x"]=-20219.009714285,}, -[3]={["y"]=-17855.415714285,["x"]=-20256.438285714,}, -[4]={["y"]=-19217.791999999,["x"]=-21283.597714285,}, -}, -}, -}, -[AIRBASE.Normandy.Beny_sur_Mer]={ -PointsRunways={ -[1]={ -[1]={["y"]=-8592.7442857133,["x"]=-20386.15542857,}, -[2]={["y"]=-8404.4931428561,["x"]=-21744.113142856,}, -[3]={["y"]=-8267.9917142847,["x"]=-21724.97742857,}, -[4]={["y"]=-8451.0482857133,["x"]=-20368.87542857,}, -}, -}, -}, -[AIRBASE.Normandy.Beuzeville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-71552.573428571,["x"]=-8744.3688571427,}, -[2]={["y"]=-72577.765714285,["x"]=-9638.5682857141,}, -[3]={["y"]=-72609.304285714,["x"]=-9601.2954285712,}, -[4]={["y"]=-71585.849428571,["x"]=-8709.9648571426,}, -}, -}, -}, -[AIRBASE.Normandy.Biniville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-84757.320285714,["x"]=-7377.1354285713,}, -[2]={["y"]=-84271.482,["x"]=-7956.4859999999,}, -[3]={["y"]=-84299.482,["x"]=-7981.6288571427,}, -[4]={["y"]=-84784.969714286,["x"]=-7402.0588571427,}, -}, -}, -}, -[AIRBASE.Normandy.Brucheville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-65546.792857142,["x"]=-14615.640857143,}, -[2]={["y"]=-66914.692,["x"]=-15232.713714285,}, -[3]={["y"]=-66896.527714285,["x"]=-15271.948571428,}, -[4]={["y"]=-65528.393714285,["x"]=-14657.995714286,}, -}, -}, -}, -[AIRBASE.Normandy.Cardonville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-54280.445428571,["x"]=-15843.749142857,}, -[2]={["y"]=-53646.998571428,["x"]=-17143.012285714,}, -[3]={["y"]=-53683.93,["x"]=-17161.317428571,}, -[4]={["y"]=-54323.354571428,["x"]=-15855.004,}, -}, -}, -}, -[AIRBASE.Normandy.Carpiquet]={ -PointsRunways={ -[1]={ -[1]={["y"]=-10751.325714285,["x"]=-34229.494,}, -[2]={["y"]=-9283.5279999993,["x"]=-35192.352857142,}, -[3]={["y"]=-9325.2005714274,["x"]=-35260.967714285,}, -[4]={["y"]=-10794.90942857,["x"]=-34287.041428571,}, -}, -}, -}, -[AIRBASE.Normandy.Chailey]={ -PointsRunways={ -[1]={ -[1]={["y"]=12895.585714292,["x"]=164683.05657144,}, -[2]={["y"]=11410.727142863,["x"]=163606.54485715,}, -[3]={["y"]=11363.012857149,["x"]=163671.97342858,}, -[4]={["y"]=12797.537142863,["x"]=164711.01857144,}, -[5]={["y"]=12862.902857149,["x"]=164726.99685715,}, -}, -[2]={ -[1]={["y"]=11805.316000006,["x"]=164502.90971429,}, -[2]={["y"]=11997.280857149,["x"]=163032.65542858,}, -[3]={["y"]=11918.640857149,["x"]=163023.04657144,}, -[4]={["y"]=11726.973428578,["x"]=164489.94257143,}, -}, -}, -}, -[AIRBASE.Normandy.Chippelle]={ -PointsRunways={ -[1]={ -[1]={["y"]=-48540.313999999,["x"]=-28884.795999999,}, -[2]={["y"]=-47251.820285713,["x"]=-28140.128571427,}, -[3]={["y"]=-47274.551714285,["x"]=-28103.758285713,}, -[4]={["y"]=-48555.657714285,["x"]=-28839.90142857,}, -}, -}, -}, -[AIRBASE.Normandy.Cretteville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-78351.723142857,["x"]=-18177.725428571,}, -[2]={["y"]=-77220.322285714,["x"]=-19125.687714286,}, -[3]={["y"]=-77247.899428571,["x"]=-19158.49,}, -[4]={["y"]=-78380.008857143,["x"]=-18208.011142857,}, -}, -}, -}, -[AIRBASE.Normandy.Cricqueville_en_Bessin]={ -PointsRunways={ -[1]={ -[1]={["y"]=-50875.034571428,["x"]=-14322.404571428,}, -[2]={["y"]=-50681.148571428,["x"]=-15825.258,}, -[3]={["y"]=-50717.434285713,["x"]=-15829.829428571,}, -[4]={["y"]=-50910.569428571,["x"]=-14327.562857142,}, -}, -}, -}, -[AIRBASE.Normandy.Deux_Jumeaux]={ -PointsRunways={ -[1]={ -[1]={["y"]=-49575.410857142,["x"]=-16575.161142857,}, -[2]={["y"]=-48149.077999999,["x"]=-16952.193428571,}, -[3]={["y"]=-48159.935142856,["x"]=-16996.764857142,}, -[4]={["y"]=-49584.839428571,["x"]=-16617.732571428,}, -}, -}, -}, -[AIRBASE.Normandy.Evreux]={ -PointsRunways={ -[1]={ -[1]={["y"]=112906.84828572,["x"]=-45585.824857142,}, -[2]={["y"]=112050.38228572,["x"]=-46811.871999999,}, -[3]={["y"]=111980.05371429,["x"]=-46762.173142856,}, -[4]={["y"]=112833.54542857,["x"]=-45540.010571428,}, -}, -[2]={ -[1]={["y"]=112046.02085714,["x"]=-45091.056571428,}, -[2]={["y"]=112488.668,["x"]=-46623.617999999,}, -[3]={["y"]=112405.66914286,["x"]=-46647.419142856,}, -[4]={["y"]=111966.03657143,["x"]=-45112.604285713,}, -}, -}, -}, -[AIRBASE.Normandy.Ford_AF]={ -PointsRunways={ -[1]={ -[1]={["y"]=-26506.13971428,["x"]=147514.39971429,}, -[2]={["y"]=-25012.977428565,["x"]=147566.14485715,}, -[3]={["y"]=-25009.851428565,["x"]=147482.63600001,}, -[4]={["y"]=-26503.693999994,["x"]=147427.33228572,}, -}, -[2]={ -[1]={["y"]=-25169.701999994,["x"]=148421.09257143,}, -[2]={["y"]=-26092.421999994,["x"]=147190.89628572,}, -[3]={["y"]=-26158.136285708,["x"]=147240.89628572,}, -[4]={["y"]=-25252.357999994,["x"]=148448.64457143,}, -}, -}, -}, -[AIRBASE.Normandy.Funtington]={ -PointsRunways={ -[1]={ -[1]={["y"]=-44698.388571423,["x"]=152952.17257143,}, -[2]={["y"]=-46452.993142851,["x"]=152388.77885714,}, -[3]={["y"]=-46476.361142851,["x"]=152470.05885714,}, -[4]={["y"]=-44787.256571423,["x"]=153009.52,}, -[5]={["y"]=-44715.581428566,["x"]=153002.08714286,}, -}, -[2]={ -[1]={["y"]=-45792.665999994,["x"]=153123.894,}, -[2]={["y"]=-46068.084857137,["x"]=151665.98342857,}, -[3]={["y"]=-46148.632285708,["x"]=151681.58685714,}, -[4]={["y"]=-45871.25971428,["x"]=153136.82714286,}, -}, -}, -}, -[AIRBASE.Normandy.Lantheuil]={ -PointsRunways={ -[1]={ -[1]={["y"]=-17158.84542857,["x"]=-24602.999428571,}, -[2]={["y"]=-15978.59342857,["x"]=-23922.978571428,}, -[3]={["y"]=-15932.021999999,["x"]=-24004.121428571,}, -[4]={["y"]=-17090.734857142,["x"]=-24673.248,}, -}, -}, -}, -[AIRBASE.Normandy.Lessay]={ -PointsRunways={ -[1]={ -[1]={["y"]=-87667.304571429,["x"]=-33220.165714286,}, -[2]={["y"]=-86146.607714286,["x"]=-34248.483142857,}, -[3]={["y"]=-86191.538285714,["x"]=-34316.991142857,}, -[4]={["y"]=-87712.212,["x"]=-33291.774857143,}, -}, -[2]={ -[1]={["y"]=-87125.123142857,["x"]=-34183.682571429,}, -[2]={["y"]=-85803.278285715,["x"]=-33498.428857143,}, -[3]={["y"]=-85768.408285715,["x"]=-33570.13,}, -[4]={["y"]=-87087.688571429,["x"]=-34258.272285715,}, -}, -}, -}, -[AIRBASE.Normandy.Lignerolles]={ -PointsRunways={ -[1]={ -[1]={["y"]=-35279.611714285,["x"]=-35232.026857142,}, -[2]={["y"]=-33804.948857142,["x"]=-35770.713999999,}, -[3]={["y"]=-33789.876285713,["x"]=-35726.655714284,}, -[4]={["y"]=-35263.548285713,["x"]=-35192.75542857,}, -}, -}, -}, -[AIRBASE.Normandy.Longues_sur_Mer]={ -PointsRunways={ -[1]={ -[1]={["y"]=-29444.070285713,["x"]=-16334.105428571,}, -[2]={["y"]=-28265.52942857,["x"]=-17011.557999999,}, -[3]={["y"]=-28344.74742857,["x"]=-17143.587999999,}, -[4]={["y"]=-29529.616285713,["x"]=-16477.766571428,}, -}, -}, -}, -[AIRBASE.Normandy.Maupertus]={ -PointsRunways={ -[1]={ -[1]={["y"]=-85605.340857143,["x"]=16175.267714286,}, -[2]={["y"]=-84132.567142857,["x"]=15895.905714286,}, -[3]={["y"]=-84139.995142857,["x"]=15847.623714286,}, -[4]={["y"]=-85613.626571429,["x"]=16132.410571429,}, -}, -}, -}, -[AIRBASE.Normandy.Meautis]={ -PointsRunways={ -[1]={ -[1]={["y"]=-72642.527714286,["x"]=-24593.622285714,}, -[2]={["y"]=-71298.672571429,["x"]=-24352.651142857,}, -[3]={["y"]=-71290.101142857,["x"]=-24398.365428571,}, -[4]={["y"]=-72631.715714286,["x"]=-24639.966857143,}, -}, -}, -}, -[AIRBASE.Normandy.Le_Molay]={ -PointsRunways={ -[1]={ -[1]={["y"]=-41876.526857142,["x"]=-26701.052285713,}, -[2]={["y"]=-40979.545714285,["x"]=-25675.045999999,}, -[3]={["y"]=-41017.687428571,["x"]=-25644.272571427,}, -[4]={["y"]=-41913.638285713,["x"]=-26665.137999999,}, -}, -}, -}, -[AIRBASE.Normandy.Needs_Oar_Point]={ -PointsRunways={ -[1]={ -[1]={["y"]=-83882.441142851,["x"]=141429.83314286,}, -[2]={["y"]=-85138.159428566,["x"]=140187.52828572,}, -[3]={["y"]=-85208.323428566,["x"]=140161.04371429,}, -[4]={["y"]=-85245.751999994,["x"]=140201.61514286,}, -[5]={["y"]=-83939.966571423,["x"]=141485.22085714,}, -}, -[2]={ -[1]={["y"]=-84528.76571428,["x"]=141988.01428572,}, -[2]={["y"]=-84116.98971428,["x"]=140565.78685714,}, -[3]={["y"]=-84199.35771428,["x"]=140541.14685714,}, -[4]={["y"]=-84605.051428566,["x"]=141966.01428572,}, -}, -}, -}, -[AIRBASE.Normandy.Picauville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-80808.838571429,["x"]=-11834.554571428,}, -[2]={["y"]=-79531.574285714,["x"]=-12311.274,}, -[3]={["y"]=-79549.355428571,["x"]=-12356.928285714,}, -[4]={["y"]=-80827.815142857,["x"]=-11901.835142857,}, -}, -}, -}, -[AIRBASE.Normandy.Rucqueville]={ -PointsRunways={ -[1]={ -[1]={["y"]=-20023.988857141,["x"]=-26569.565428571,}, -[2]={["y"]=-18688.92542857,["x"]=-26571.086571428,}, -[3]={["y"]=-18688.012571427,["x"]=-26611.252285713,}, -[4]={["y"]=-20022.218857141,["x"]=-26608.505428571,}, -}, -}, -}, -[AIRBASE.Normandy.Saint_Pierre_du_Mont]={ -PointsRunways={ -[1]={ -[1]={["y"]=-48015.384571428,["x"]=-11886.631714285,}, -[2]={["y"]=-46540.412285713,["x"]=-11945.226571428,}, -[3]={["y"]=-46541.349999999,["x"]=-11991.174571428,}, -[4]={["y"]=-48016.837142856,["x"]=-11929.371142857,}, -}, -}, -}, -[AIRBASE.Normandy.Sainte_Croix_sur_Mer]={ -PointsRunways={ -[1]={ -[1]={["y"]=-15877.817999999,["x"]=-18812.579999999,}, -[2]={["y"]=-14464.377142856,["x"]=-18807.46,}, -[3]={["y"]=-14463.879714285,["x"]=-18759.706857142,}, -[4]={["y"]=-15878.229142856,["x"]=-18764.071428571,}, -}, -}, -}, -[AIRBASE.Normandy.Sainte_Laurent_sur_Mer]={ -PointsRunways={ -[1]={ -[1]={["y"]=-41676.834857142,["x"]=-14475.109428571,}, -[2]={["y"]=-40566.11142857,["x"]=-14817.319999999,}, -[3]={["y"]=-40579.543999999,["x"]=-14860.059999999,}, -[4]={["y"]=-41687.120571427,["x"]=-14509.680857142,}, -}, -}, -}, -[AIRBASE.Normandy.Sommervieu]={ -PointsRunways={ -[1]={ -[1]={["y"]=-26821.913714284,["x"]=-21390.466571427,}, -[2]={["y"]=-25465.308857142,["x"]=-21296.859999999,}, -[3]={["y"]=-25462.451714284,["x"]=-21343.717142856,}, -[4]={["y"]=-26818.002285713,["x"]=-21440.532857142,}, -}, -}, -}, -[AIRBASE.Normandy.Tangmere]={ -PointsRunways={ -[1]={ -[1]={["y"]=-34684.581142851,["x"]=150459.61657143,}, -[2]={["y"]=-33250.625428566,["x"]=149954.17,}, -[3]={["y"]=-33275.724285708,["x"]=149874.69028572,}, -[4]={["y"]=-34709.020571423,["x"]=150377.93742857,}, -}, -[2]={ -[1]={["y"]=-33103.438857137,["x"]=150812.72542857,}, -[2]={["y"]=-34410.246285708,["x"]=150009.73142857,}, -[3]={["y"]=-34453.535142851,["x"]=150082.02685714,}, -[4]={["y"]=-33176.545999994,["x"]=150870.22542857,}, -}, -}, -}, -[AIRBASE.Normandy.Argentan]={ -PointsRunways={ -[1]={ -[1]={["y"]=22322.280338032,["x"]=-78607.309765269,}, -[2]={["y"]=23032.778713963,["x"]=-78967.17709893,}, -[3]={["y"]=23015.27074041,["x"]=-79008.02903722,}, -[4]={["y"]=22299.944963827,["x"]=-78650.366148928,}, -}, -}, -}, -[AIRBASE.Normandy.Goulet]={ -PointsRunways={ -[1]={ -[1]={["y"]=24901.788373185,["x"]=-89139.367511763,}, -[2]={["y"]=25459.965967043,["x"]=-89709.67940114,}, -[3]={["y"]=25422.459962713,["x"]=-89741.669816598,}, -[4]={["y"]=24857.663662208,["x"]=-89173.56416277,}, -}, -}, -}, -[AIRBASE.Normandy.Essay]={ -PointsRunways={ -[1]={ -[1]={["y"]=44610.072022849,["x"]=-105469.21149064,}, -[2]={["y"]=45417.939023956,["x"]=-105536.08535277,}, -[3]={["y"]=45412.558368383,["x"]=-105585.27991801,}, -[4]={["y"]=44602.38537203,["x"]=-105516.10006064,}, -}, -}, -}, -[AIRBASE.Normandy.Hauterive]={ -PointsRunways={ -[1]={ -[1]={["y"]=40617.185360953,["x"]=-107657.10147517,}, -[2]={["y"]=41114.628372034,["x"]=-108298.77015609,}, -[3]={["y"]=41080.006684855,["x"]=-108319.06562788,}, -[4]={["y"]=40584.558402807,["x"]=-107692.29370481,}, -}, -}, -}, -[AIRBASE.Normandy.Vrigny]={ -PointsRunways={ -[1]={ -[1]={["y"]=24892.131051827,["x"]=-89131.628297486,}, -[2]={["y"]=25469.738000575,["x"]=-89709.235246234,}, -[3]={["y"]=25418.869206793,["x"]=-89738.771965204,}, -[4]={["y"]=24859.312475193,["x"]=-89171.010589446,}, -}, -}, -}, -[AIRBASE.Normandy.Barville]={ -PointsRunways={ -[1]={ -[1]={["y"]=49027.850333166,["x"]=-109217.05049066,}, -[2]={["y"]=49755.022185805,["x"]=-110346.63783457,}, -[3]={["y"]=49682.657996586,["x"]=-110401.35222154,}, -[4]={["y"]=48921.951519675,["x"]=-109285.88471943,}, -}, -[2]={ -[1]={["y"]=48429.522036941,["x"]=-109818.90874734,}, -[2]={["y"]=49746.197284681,["x"]=-109954.81222465,}, -[3]={["y"]=49735.607403332,["x"]=-110032.47135455,}, -[4]={["y"]=48420.697135816,["x"]=-109900.09783768,}, -}, -}, -}, -[AIRBASE.Normandy.Conches]={ -PointsRunways={ -[1]={ -[1]={["y"]=95099.187473266,["x"]=-56389.619005858,}, -[2]={["y"]=95181.545025963,["x"]=-56465.440244849,}, -[3]={["y"]=94071.678958666,["x"]=-57627.596821795,}, -[4]={["y"]=94005.008558864,["x"]=-57558.31189651,}, -}, -}, -}, -}, -} -function ATC_GROUND_NORMANDY:New(AirbaseNames) -local self=BASE:Inherit(self,ATC_GROUND:New(self.Airbases,AirbaseNames)) -self:SetKickSpeedKmph(40) -self:SetMaximumKickSpeedKmph(100) -return self -end -function ATC_GROUND_NORMANDY:Start(RepeatScanSeconds) -RepeatScanSeconds=RepeatScanSeconds or 0.05 -self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds) -end -ATC_GROUND_PERSIANGULF={ -ClassName="ATC_GROUND_PERSIANGULF", -Airbases={ -[AIRBASE.PersianGulf.Abu_Musa_Island_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-122813.71002344,["x"]=-31689.936027827,}, -[2]={["y"]=-122827.82488722,["x"]=-31590.105445836,}, -[3]={["y"]=-122769.5689949,["x"]=-31583.176330891,}, -[4]={["y"]=-122726.96776968,["x"]=-31614.998932862,}, -[5]={["y"]=-121293.92414543,["x"]=-31467.947715689,}, -[6]={["y"]=-121296.4904843,["x"]=-31432.018971528,}, -[7]={["y"]=-121236.18152088,["x"]=-31424.576588809,}, -[8]={["y"]=-121190.50068902,["x"]=-31458.452261875,}, -[9]={["y"]=-119839.83654246,["x"]=-31319.356695194,}, -[10]={["y"]=-119824.69514313,["x"]=-31423.293419374,}, -[11]={["y"]=-119886.80054375,["x"]=-31430.22253432,}, -[12]={["y"]=-119932.22474173,["x"]=-31395.320325706,}, -[13]={["y"]=-122813.9472789,["x"]=-31689.81193251,}, -}, -}, -}, -[AIRBASE.PersianGulf.Al_Dhafra_AB]={ -PointsRunways={ -[1]={ -[1]={["y"]=-174672.06004916,["x"]=-209880.97145616,}, -[2]={["y"]=-174705.15693282,["x"]=-209923.15131918,}, -[3]={["y"]=-171819.05380065,["x"]=-212172.84298281,}, -[4]={["y"]=-171785.09826475,["x"]=-212129.87417284,}, -[5]={["y"]=-174671.96413454,["x"]=-209880.52453983,}, -}, -[2]={ -[1]={["y"]=-174351.95872272,["x"]=-211813.88516693,}, -[2]={["y"]=-174381.29169939,["x"]=-211851.81242636,}, -[3]={["y"]=-171493.65648904,["x"]=-214102.92235002,}, -[4]={["y"]=-171464.99693831,["x"]=-214062.78788361,}, -[5]={["y"]=-174351.8628081,["x"]=-211813.4382506,}, -}, -}, -}, -[AIRBASE.PersianGulf.Al_Maktoum_Intl]={ -PointsRunways={ -[1]={ -[1]={["y"]=-111879.49046471,["x"]=-138953.80105841,}, -[2]={["y"]=-111917.23447224,["x"]=-139018.2804046,}, -[3]={["y"]=-108092.98121312,["x"]=-141406.67838426,}, -[4]={["y"]=-108052.34416748,["x"]=-141341.82058294,}, -[5]={["y"]=-111879.5412879,["x"]=-138952.87693763,}, -}, -}, -}, -[AIRBASE.PersianGulf.Al_Minhad_AB]={ -PointsRunways={ -[1]={ -[1]={["y"]=-91070.628933035,["x"]=-125989.64095162,}, -[2]={["y"]=-91072.346560159,["x"]=-126040.59722299,}, -[3]={["y"]=-87098.282779771,["x"]=-126039.41747017,}, -[4]={["y"]=-87099.632735396,["x"]=-125991.26905291,}, -[5]={["y"]=-91071.031270042,["x"]=-125987.44617225,}, -}, -}, -}, -[AIRBASE.PersianGulf.Bandar_Abbas_Intl]={ -PointsRunways={ -[1]={ -[1]={["y"]=12988.484058788,["x"]=113979.99250505,}, -[2]={["y"]=13037.8836239,["x"]=113952.60241152,}, -[3]={["y"]=14877.313199902,["x"]=117414.37833333,}, -[4]={["y"]=14828.777486364,["x"]=117439.06043783,}, -[5]={["y"]=12988.939584604,["x"]=113979.52494386,}, -}, -[2]={ -[1]={["y"]=13203.406014284,["x"]=113848.44907555,}, -[2]={["y"]=13258.268500181,["x"]=113818.47303925,}, -[3]={["y"]=15315.015323566,["x"]=117694.27156647,}, -[4]={["y"]=15264.815746383,["x"]=117725.22168173,}, -[5]={["y"]=13203.861540099,["x"]=113847.98151436,}, -}, -}, -}, -[AIRBASE.PersianGulf.Bandar_Lengeh]={ -PointsRunways={ -[1]={ -[1]={["y"]=-142373.15541415,["x"]=41364.94047809,}, -[2]={["y"]=-142363.30071107,["x"]=41298.112282592,}, -[3]={["y"]=-142217.57151662,["x"]=41320.35666061,}, -[4]={["y"]=-142213.00856728,["x"]=41291.838227254,}, -[5]={["y"]=-142131.44584788,["x"]=41301.534494595,}, -[6]={["y"]=-142132.58658522,["x"]=41323.778872613,}, -[7]={["y"]=-142123.17550221,["x"]=41336.041798956,}, -[8]={["y"]=-139580.45381288,["x"]=41711.022304533,}, -[9]={["y"]=-139590.04241918,["x"]=41778.350996659,}, -[10]={["y"]=-139732.41237808,["x"]=41757.089304408,}, -[11]={["y"]=-139736.7897853,["x"]=41785.646675372,}, -[12]={["y"]=-139816.41690726,["x"]=41775.641173137,}, -[13]={["y"]=-139816.00001133,["x"]=41754.58792885,}, -[14]={["y"]=-139824.1294819,["x"]=41743.748634761,}, -[15]={["y"]=-142373.20183966,["x"]=41365.161507021,}, -}, -}, -}, -[AIRBASE.PersianGulf.Dubai_Intl]={ -PointsRunways={ -[1]={ -[1]={["y"]=-89693.511670714,["x"]=-100490.47082052,}, -[2]={["y"]=-89731.488328846,["x"]=-100555.50584758,}, -[3]={["y"]=-85706.437275049,["x"]=-103076.68123933,}, -[4]={["y"]=-85669.519216262,["x"]=-103010.44994755,}, -[5]={["y"]=-89693.036962487,["x"]=-100489.9961123,}, -}, -[2]={ -[1]={["y"]=-90797.505501889,["x"]=-99344.082465487,}, -[2]={["y"]=-90835.482160021,["x"]=-99409.11749254,}, -[3]={["y"]=-87210.216900398,["x"]=-101681.72494832,}, -[4]={["y"]=-87171.474397253,["x"]=-101619.20256393,}, -[5]={["y"]=-90797.030793662,["x"]=-99343.607757261,}, -}, -}, -}, -[AIRBASE.PersianGulf.Fujairah_Intl]={ -PointsRunways={ -[1]={ -[1]={["y"]=5808.8716147284,["x"]=-116602.15633995,}, -[2]={["y"]=5781.9885293892,["x"]=-116666.67574476,}, -[3]={["y"]=9435.1910907931,["x"]=-118192.91910235,}, -[4]={["y"]=9459.878635843,["x"]=-118134.40047704,}, -[5]={["y"]=5808.4078522575,["x"]=-116603.31550719,}, -}, -}, -}, -[AIRBASE.PersianGulf.Havadarya]={ -PointsRunways={ -[1]={ -[1]={["y"]=-7565.4887830428,["x"]=109074.13162774,}, -[2]={["y"]=-7557.8281079193,["x"]=109030.65729641,}, -[3]={["y"]=-4987.3556518085,["x"]=109524.49147773,}, -[4]={["y"]=-4996.215358578,["x"]=109566.57508489,}, -[5]={["y"]=-7565.4936338604,["x"]=109074.32262205,}, -}, -}, -}, -[AIRBASE.PersianGulf.Kerman_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=70375.468628778,["x"]=456046.12685302,}, -[2]={["y"]=70297.050081575,["x"]=456015.1578105,}, -[3]={["y"]=71814.291673715,["x"]=452165.51037702,}, -[4]={["y"]=71902.918622452,["x"]=452188.46411914,}, -[5]={["y"]=70860.465673482,["x"]=454829.89695989,}, -[6]={["y"]=70862.525255971,["x"]=454892.77675983,}, -[7]={["y"]=70816.157465062,["x"]=454922.77944807,}, -[8]={["y"]=70462.749176371,["x"]=455833.38051827,}, -[9]={["y"]=70483.400377364,["x"]=455901.17880077,}, -[10]={["y"]=70453.787334431,["x"]=455974.8217628,}, -[11]={["y"]=70405.860962315,["x"]=455961.57382254,}, -[12]={["y"]=70374.689338175,["x"]=456046.51649833,}, -}, -}, -}, -[AIRBASE.PersianGulf.Khasab]={ -PointsRunways={ -[1]={ -[1]={["y"]=-534.81827307392,["x"]=-1495.070060483,}, -[2]={["y"]=-434.82912685139,["x"]=-1519.8421462589,}, -[3]={["y"]=-405.55302547993,["x"]=-1413.0969766429,}, -[4]={["y"]=-424.92029254105,["x"]=-1352.0675653224,}, -[5]={["y"]=216.05735069389,["x"]=1206.9187095195,}, -[6]={["y"]=116.42961315781,["x"]=1229.9576238247,}, -[7]={["y"]=88.253643635887,["x"]=1123.7918160128,}, -[8]={["y"]=101.1741158476,["x"]=1042.6886109249,}, -[9]={["y"]=-535.31436058928,["x"]=-1494.8762081291,}, -}, -}, -}, -[AIRBASE.PersianGulf.Lar_Airbase]={ -PointsRunways={ -[1]={ -[1]={["y"]=-183987.5454359,["x"]=169021.72039309,}, -[2]={["y"]=-183988.41292374,["x"]=168955.27082471,}, -[3]={["y"]=-180847.92031188,["x"]=168930.46175795,}, -[4]={["y"]=-180806.58653731,["x"]=168888.39641215,}, -[5]={["y"]=-180740.37934087,["x"]=168886.56748407,}, -[6]={["y"]=-180735.62412787,["x"]=168932.65647164,}, -[7]={["y"]=-180685.14571291,["x"]=168934.11961411,}, -[8]={["y"]=-180682.5852136,["x"]=169001.78995301,}, -[9]={["y"]=-183987.48111493,["x"]=169021.35002828,}, -}, -}, -}, -[AIRBASE.PersianGulf.Qeshm_Island]={ -PointsRunways={ -[1]={ -[1]={["y"]=-35140.372717152,["x"]=63373.658918509,}, -[2]={["y"]=-35098.556715749,["x"]=63320.377239302,}, -[3]={["y"]=-34991.318905699,["x"]=63408.730403557,}, -[4]={["y"]=-34984.574389344,["x"]=63401.311435566,}, -[5]={["y"]=-34991.993357335,["x"]=63313.632722947,}, -[6]={["y"]=-34956.921872287,["x"]=63265.746656824,}, -[7]={["y"]=-34917.129225791,["x"]=63261.699947011,}, -[8]={["y"]=-34832.822771349,["x"]=63337.23853019,}, -[9]={["y"]=-34915.105870884,["x"]=63436.382920614,}, -[10]={["y"]=-34906.337999622,["x"]=63478.198922017,}, -[11]={["y"]=-32728.533668488,["x"]=65307.986209216,}, -[12]={["y"]=-32676.600892552,["x"]=65299.218337954,}, -[13]={["y"]=-32623.99366498,["x"]=65334.964274638,}, -[14]={["y"]=-32626.691471522,["x"]=65388.92040548,}, -[15]={["y"]=-31822.745121968,["x"]=66067.418750826,}, -[16]={["y"]=-31777.556862387,["x"]=66068.767654097,}, -[17]={["y"]=-31691.227053039,["x"]=65974.344425122,}, -[18]={["y"]=-31606.246146962,["x"]=66042.464040311,}, -[19]={["y"]=-31602.199437148,["x"]=66084.280041714,}, -[20]={["y"]=-31632.549760747,["x"]=66124.747139846,}, -[21]={["y"]=-31727.647441358,["x"]=66134.189462744,}, -[22]={["y"]=-31734.391957713,["x"]=66141.608430735,}, -[23]={["y"]=-31632.549760747,["x"]=66225.914885176,}, -[24]={["y"]=-31673.691310515,["x"]=66277.173209477,}, -[25]={["y"]=-35140.880825624,["x"]=63373.905965825,}, -}, -}, -}, -[AIRBASE.PersianGulf.Sharjah_Intl]={ -PointsRunways={ -[1]={ -[1]={["y"]=-71668.808658476,["x"]=-93980.156242153,}, -[2]={["y"]=-75307.847363315,["x"]=-91617.097584505,}, -[3]={["y"]=-75280.458023829,["x"]=-91574.709321014,}, -[4]={["y"]=-72249.697184234,["x"]=-93529.134331507,}, -[5]={["y"]=-72179.919581256,["x"]=-93526.199759419,}, -[6]={["y"]=-72138.183444896,["x"]=-93597.933743788,}, -[7]={["y"]=-71638.654062835,["x"]=-93927.584008321,}, -[8]={["y"]=-71668.325847279,["x"]=-93979.428115206,}, -}, -[2]={ -[1]={["y"]=-71553.225408723,["x"]=-93775.312323319,}, -[2]={["y"]=-75168.13829548,["x"]=-91426.51571111,}, -[3]={["y"]=-75125.388157445,["x"]=-91363.754870166,}, -[4]={["y"]=-71510.511081666,["x"]=-93703.252275385,}, -[5]={["y"]=-71552.247218027,["x"]=-93775.638386885,}, -}, -}, -}, -[AIRBASE.PersianGulf.Shiraz_International_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-353995.75579778,["x"]=382327.42294273,}, -[2]={["y"]=-354029.77009807,["x"]=382265.46199492,}, -[3]={["y"]=-349407.98049238,["x"]=379941.14030526,}, -[4]={["y"]=-349376.87025024,["x"]=380004.69408564,}, -[5]={["y"]=-353995.71101815,["x"]=382327.59771695,}, -}, -[2]={ -[1]={["y"]=-354056.29510012,["x"]=381845.97598829,}, -[2]={["y"]=-354091.48797289,["x"]=381783.6025623,}, -[3]={["y"]=-349650.64038107,["x"]=379550.92898242,}, -[4]={["y"]=-349624.41889127,["x"]=379614.92719482,}, -[5]={["y"]=-354056.25032049,["x"]=381846.15076251,}, -}, -}, -}, -[AIRBASE.PersianGulf.Sir_Abu_Nuayr]={ -PointsRunways={ -[1]={ -[1]={["y"]=-203367.3128691,["x"]=-103017.22553918,}, -[2]={["y"]=-203373.59664477,["x"]=-103054.92819323,}, -[3]={["y"]=-202578.27577922,["x"]=-103188.26018333,}, -[4]={["y"]=-202571.37254488,["x"]=-103151.01482599,}, -[5]={["y"]=-203367.65259839,["x"]=-103016.48202662,}, -[6]={["y"]=-203291.39594004,["x"]=-102985.49774228,}, -}, -}, -}, -[AIRBASE.PersianGulf.Sirri_Island]={ -PointsRunways={ -[1]={ -[1]={["y"]=-169713.12842428,["x"]=-27766.658020853,}, -[2]={["y"]=-169682.02009414,["x"]=-27726.583172021,}, -[3]={["y"]=-169727.21866794,["x"]=-27691.632048154,}, -[4]={["y"]=-169694.28043602,["x"]=-27650.276268081,}, -[5]={["y"]=-169763.08474269,["x"]=-27598.490047901,}, -[6]={["y"]=-169825.30140298,["x"]=-27607.090586235,}, -[7]={["y"]=-171614.98889813,["x"]=-26246.247907014,}, -[8]={["y"]=-171620.85326172,["x"]=-26187.105176343,}, -[9]={["y"]=-171686.10990337,["x"]=-26138.56820961,}, -[10]={["y"]=-171716.55468456,["x"]=-26178.745338885,}, -[11]={["y"]=-171764.9668776,["x"]=-26142.810515186,}, -[12]={["y"]=-171796.29599657,["x"]=-26183.416460911,}, -[13]={["y"]=-169713.5628285,["x"]=-27766.883787223,}, -}, -}, -}, -[AIRBASE.PersianGulf.Tunb_Island_AFB]={ -PointsRunways={ -[1]={ -[1]={["y"]=-92923.634698863,["x"]=9547.6862547173,}, -[2]={["y"]=-92963.030803298,["x"]=9565.7274614215,}, -[3]={["y"]=-92934.128053782,["x"]=9619.2987996964,}, -[4]={["y"]=-92970.946842975,["x"]=9640.1014155901,}, -[5]={["y"]=-92949.591945243,["x"]=9682.8112110532,}, -[6]={["y"]=-92899.518391942,["x"]=9699.7478540817,}, -[7]={["y"]=-91969.13471408,["x"]=11464.627292768,}, -[8]={["y"]=-91983.666755417,["x"]=11515.293058512,}, -[9]={["y"]=-91960.101282978,["x"]=11557.710908902,}, -[10]={["y"]=-91921.021874517,["x"]=11539.251288825,}, -[11]={["y"]=-91893.725202275,["x"]=11589.720675632,}, -[12]={["y"]=-91859.751646175,["x"]=11571.850192366,}, -[13]={["y"]=-92922.149728329,["x"]=9547.2937058617,}, -}, -}, -}, -[AIRBASE.PersianGulf.Tunb_Kochak]={ -PointsRunways={ -[1]={ -[1]={["y"]=-109925.50271188,["x"]=8974.5666013181,}, -[2]={["y"]=-109905.7382908,["x"]=8937.53274444,}, -[3]={["y"]=-109009.93726324,["x"]=9072.2234968343,}, -[4]={["y"]=-109040.82867587,["x"]=9104.9871291834,}, -[5]={["y"]=-109925.26515172,["x"]=8974.091480998,}, -}, -}, -}, -[AIRBASE.PersianGulf.Sas_Al_Nakheel_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-176230.75865538,["x"]=-188732.01369812,}, -[2]={["y"]=-176274.78045186,["x"]=-188744.8049371,}, -[3]={["y"]=-175692.03171595,["x"]=-190564.17145168,}, -[4]={["y"]=-175649.7486572,["x"]=-190550.58435053,}, -[5]={["y"]=-176230.66274076,["x"]=-188731.5667818,}, -}, -}, -}, -[AIRBASE.PersianGulf.Bandar_e_Jask_airfield]={ -PointsRunways={ -[1]={ -[1]={["y"]=155156.73167657,["x"]=-57837.031277333,}, -[2]={["y"]=155130.38996239,["x"]=-57790.475605714,}, -[3]={["y"]=157137.17872571,["x"]=-56710.411783359,}, -[4]={["y"]=157148.46631801,["x"]=-56688.071756941,}, -[5]={["y"]=157220.07198163,["x"]=-56649.035500253,}, -[6]={["y"]=157227.83220133,["x"]=-56662.204357931,}, -[7]={["y"]=157359.6383572,["x"]=-56590.481115222,}, -[8]={["y"]=157383.03659539,["x"]=-56633.044744502,}, -[9]={["y"]=155156.7940421,["x"]=-57837.149989814,}, -}, -}, -}, -[AIRBASE.PersianGulf.Abu_Dhabi_International_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-163964.56943899,["x"]=-189427.63621921,}, -[2]={["y"]=-164005.96838287,["x"]=-189478.90226888,}, -[3]={["y"]=-160798.22080495,["x"]=-192054.59531727,}, -[4]={["y"]=-160755.05282258,["x"]=-192002.58569997,}, -[5]={["y"]=-163964.47352437,["x"]=-189427.18930288,}, -}, -[2]={ -[1]={["y"]=-163615.44952024,["x"]=-187144.00786922,}, -[2]={["y"]=-163656.84846411,["x"]=-187195.27391888,}, -[3]={["y"]=-160452.71811093,["x"]=-189764.86593382,}, -[4]={["y"]=-160411.94568221,["x"]=-189715.47961171,}, -[5]={["y"]=-163615.35360562,["x"]=-187143.56095289,}, -}, -}, -}, -[AIRBASE.PersianGulf.Al_Bateen_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-183207.51774197,["x"]=-189871.8319832,}, -[2]={["y"]=-183240.61462564,["x"]=-189914.01184622,}, -[3]={["y"]=-180748.88998479,["x"]=-191943.30402837,}, -[4]={["y"]=-180711.83076051,["x"]=-191896.52435182,}, -[5]={["y"]=-183207.42182735,["x"]=-189871.38506688,}, -}, -}, -}, -[AIRBASE.PersianGulf.Kish_International_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-227330.79164594,["x"]=42691.91536494,}, -[2]={["y"]=-227321.58531968,["x"]=42758.113234714,}, -[3]={["y"]=-223235.73004619,["x"]=42313.579195302,}, -[4]={["y"]=-223240.99080406,["x"]=42247.819722016,}, -[5]={["y"]=-227330.67774245,["x"]=42691.785682556,}, -}, -[2]={ -[1]={["y"]=-227283.77911886,["x"]=42987.748941936,}, -[2]={["y"]=-227274.5727926,["x"]=43053.946811711,}, -[3]={["y"]=-222907.94761294,["x"]=42580.826755904,}, -[4]={["y"]=-222915.76510871,["x"]=42514.58376547,}, -[5]={["y"]=-227283.66521537,["x"]=42987.619259553,}, -}, -}, -}, -[AIRBASE.PersianGulf.Al_Ain_International_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-65165.315648901,["x"]=-209042.45716363,}, -[2]={["y"]=-65112.933878375,["x"]=-209048.84518442,}, -[3]={["y"]=-65672.013626755,["x"]=-213019.66479976,}, -[4]={["y"]=-65722.555424932,["x"]=-213013.91596964,}, -[5]={["y"]=-65165.400582791,["x"]=-209042.15059908,}, -}, -}, -}, -[AIRBASE.PersianGulf.Lavan_Island_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=-288099.83301495,["x"]=76353.443273049,}, -[2]={["y"]=-288119.51457685,["x"]=76302.756224611,}, -[3]={["y"]=-288070.96603401,["x"]=76283.898526152,}, -[4]={["y"]=-288085.61084238,["x"]=76247.386812114,}, -[5]={["y"]=-288032.04695421,["x"]=76224.316223573,}, -[6]={["y"]=-287991.12173627,["x"]=76245.38067398,}, -[7]={["y"]=-287489.96435675,["x"]=76037.610404141,}, -[8]={["y"]=-287497.65444594,["x"]=76017.686082159,}, -[9]={["y"]=-287453.61120787,["x"]=75998.111309685,}, -[10]={["y"]=-287419.70490555,["x"]=76007.199596905,}, -[11]={["y"]=-285642.24565503,["x"]=75279.787069797,}, -[12]={["y"]=-285625.46727862,["x"]=75239.239326815,}, -[13]={["y"]=-285570.23845628,["x"]=75217.217707782,}, -[14]={["y"]=-285555.20782742,["x"]=75252.172658628,}, -[15]={["y"]=-285505.92134673,["x"]=75231.199688121,}, -[16]={["y"]=-285484.28380792,["x"]=75284.258832895,}, -[17]={["y"]=-288099.97979219,["x"]=76354.32393647,}, -}, -}, -}, -[AIRBASE.PersianGulf.Jiroft_Airport]={ -PointsRunways={ -[1]={ -[1]={["y"]=140376.87310595,["x"]=283748.07558774,}, -[2]={["y"]=140299.43760975,["x"]=283655.81201779,}, -[3]={["y"]=143008.43807723,["x"]=281517.41347718,}, -[4]={["y"]=143052.6952428,["x"]=281573.25195709,}, -[5]={["y"]=142946.60213095,["x"]=281656.5960586,}, -[6]={["y"]=142975.14179847,["x"]=281687.20381796,}, -[7]={["y"]=142932.12548801,["x"]=281724.01585287,}, -[8]={["y"]=142870.49635092,["x"]=281719.05243244,}, -[9]={["y"]=140437.35783025,["x"]=283640.84253664,}, -[10]={["y"]=140433.27045062,["x"]=283705.80267729,}, -[11]={["y"]=140376.77702493,["x"]=283747.8442964,}, -}, -}, -}, -}, -} -function ATC_GROUND_PERSIANGULF:New(AirbaseNames) -local self=BASE:Inherit(self,ATC_GROUND:New(self.Airbases,AirbaseNames)) -self:SetKickSpeedKmph(50) -self:SetMaximumKickSpeedKmph(150) -return self -end -function ATC_GROUND_PERSIANGULF:Start(RepeatScanSeconds) -RepeatScanSeconds=RepeatScanSeconds or 0.05 -self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds) -end -do -DETECTION_BASE={ -ClassName="DETECTION_BASE", -DetectionSetGroup=nil, -DetectionRange=nil, -DetectedObjects={}, -DetectionRun=0, -DetectedObjectsIdentified={}, -DetectedItems={}, -DetectedItemsByIndex={}, -} -function DETECTION_BASE:New(DetectionSet) -local self=BASE:Inherit(self,FSM:New()) -self.DetectedItemCount=0 -self.DetectedItemMax=0 -self.DetectedItems={} -self.DetectionSet=DetectionSet -self.RefreshTimeInterval=30 -self:InitDetectVisual(nil) -self:InitDetectOptical(nil) -self:InitDetectRadar(nil) -self:InitDetectRWR(nil) -self:InitDetectIRST(nil) -self:InitDetectDLINK(nil) -self:FilterCategories({ -Unit.Category.AIRPLANE, -Unit.Category.GROUND_UNIT, -Unit.Category.HELICOPTER, -Unit.Category.SHIP, -Unit.Category.STRUCTURE -}) -self:SetFriendliesRange(6000) -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Detecting") -self:AddTransition("Detecting","Detect","Detecting") -self:AddTransition("Detecting","Detection","Detecting") -self:AddTransition("Detecting","Detected","Detecting") -self:AddTransition("Detecting","DetectedItem","Detecting") -self:AddTransition("*","Stop","Stopped") -return self -end -do -function DETECTION_BASE:onafterStart(From,Event,To) -self:__Detect(1) -end -function DETECTION_BASE:onafterDetect(From,Event,To) -local DetectDelay=0.1 -self.DetectionCount=0 -self.DetectionRun=0 -self:UnIdentifyAllDetectedObjects() -local DetectionTimeStamp=timer.getTime() -for DetectionObjectName,DetectedObjectData in pairs(self.DetectedObjects)do -self.DetectedObjects[DetectionObjectName].IsDetected=false -self.DetectedObjects[DetectionObjectName].IsVisible=false -self.DetectedObjects[DetectionObjectName].KnowDistance=nil -self.DetectedObjects[DetectionObjectName].LastTime=nil -self.DetectedObjects[DetectionObjectName].LastPos=nil -self.DetectedObjects[DetectionObjectName].LastVelocity=nil -self.DetectedObjects[DetectionObjectName].Distance=10000000 -end -self.DetectionCount=self:CountAliveRecce() -local DetectionInterval=self.DetectionCount/(self.RefreshTimeInterval-1) -self:ForEachAliveRecce( -function(DetectionGroup) -self:__Detection(DetectDelay,DetectionGroup,DetectionTimeStamp) -DetectDelay=DetectDelay+DetectionInterval -end -) -self:__Detect(-self.RefreshTimeInterval) -end -function DETECTION_BASE:CountAliveRecce() -return self.DetectionSet:CountAlive() -end -function DETECTION_BASE:ForEachAliveRecce(IteratorFunction,...) -self:F2(arg) -self.DetectionSet:ForEachGroupAlive(IteratorFunction,arg) -return self -end -function DETECTION_BASE:onafterDetection(From,Event,To,Detection,DetectionTimeStamp) -self.DetectionRun=self.DetectionRun+1 -local HasDetectedObjects=false -if Detection and Detection:IsAlive()then -local DetectionGroupName=Detection:GetName() -local DetectionUnit=Detection:GetUnit(1) -local DetectedUnits={} -local DetectedTargets=Detection:GetDetectedTargets( -self.DetectVisual, -self.DetectOptical, -self.DetectRadar, -self.DetectIRST, -self.DetectRWR, -self.DetectDLINK -) -self:F({DetectedTargets=DetectedTargets}) -for DetectionObjectID,Detection in pairs(DetectedTargets)do -local DetectedObject=Detection.object -if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then -local DetectedObjectName=DetectedObject:getName() -if not self.DetectedObjects[DetectedObjectName]then -self.DetectedObjects[DetectedObjectName]=self.DetectedObjects[DetectedObjectName]or{} -self.DetectedObjects[DetectedObjectName].Name=DetectedObjectName -self.DetectedObjects[DetectedObjectName].Object=DetectedObject -end -end -end -for DetectionObjectName,DetectedObjectData in pairs(self.DetectedObjects)do -local DetectedObject=DetectedObjectData.Object -if DetectedObject:isExist()then -local TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity=DetectionUnit:IsTargetDetected( -DetectedObject, -self.DetectVisual, -self.DetectOptical, -self.DetectRadar, -self.DetectIRST, -self.DetectRWR, -self.DetectDLINK -) -local DetectionAccepted=true -local DetectedObjectName=DetectedObject:getName() -local DetectedObjectType=DetectedObject:getTypeName() -local DetectedObjectVec3=DetectedObject:getPoint() -local DetectedObjectVec2={x=DetectedObjectVec3.x,y=DetectedObjectVec3.z} -local DetectionGroupVec3=Detection:GetVec3() -local DetectionGroupVec2={x=DetectionGroupVec3.x,y=DetectionGroupVec3.z} -local Distance=((DetectedObjectVec3.x-DetectionGroupVec3.x)^2+ -(DetectedObjectVec3.y-DetectionGroupVec3.y)^2+ -(DetectedObjectVec3.z-DetectionGroupVec3.z)^2 -)^0.5/1000 -local DetectedUnitCategory=DetectedObject:getDesc().category -DetectionAccepted=self._.FilterCategories[DetectedUnitCategory]~=nil and DetectionAccepted or false -if self.AcceptRange and Distance*1000>self.AcceptRange then -DetectionAccepted=false -end -if self.AcceptZones then -local AnyZoneDetection=false -for AcceptZoneID,AcceptZone in pairs(self.AcceptZones)do -local AcceptZone=AcceptZone -if AcceptZone:IsVec2InZone(DetectedObjectVec2)then -AnyZoneDetection=true -end -end -if not AnyZoneDetection then -DetectionAccepted=false -end -end -if self.RejectZones then -for RejectZoneID,RejectZone in pairs(self.RejectZones)do -local RejectZone=RejectZone -if RejectZone:IsPointVec2InZone(DetectedObjectVec2)==true then -DetectionAccepted=false -end -end -end -if not self.DetectedObjects[DetectedObjectName]and TargetIsVisible and self.DistanceProbability then -local DistanceFactor=Distance/4 -local DistanceProbabilityReversed=(1-self.DistanceProbability)*DistanceFactor -local DistanceProbability=1-DistanceProbabilityReversed -DistanceProbability=DistanceProbability*30/300 -local Probability=math.random() -if Probability>DistanceProbability then -DetectionAccepted=false -end -end -if not self.DetectedObjects[DetectedObjectName]and TargetIsVisible and self.AlphaAngleProbability then -local NormalVec2={x=DetectedObjectVec2.x-DetectionGroupVec2.x,y=DetectedObjectVec2.y-DetectionGroupVec2.y} -local AlphaAngle=math.atan2(NormalVec2.y,NormalVec2.x) -local Sinus=math.sin(AlphaAngle) -local AlphaAngleProbabilityReversed=(1-self.AlphaAngleProbability)*(1-Sinus) -local AlphaAngleProbability=1-AlphaAngleProbabilityReversed -AlphaAngleProbability=AlphaAngleProbability*30/300 -local Probability=math.random() -if Probability>AlphaAngleProbability then -DetectionAccepted=false -end -end -if not self.DetectedObjects[DetectedObjectName]and TargetIsVisible and self.ZoneProbability then -for ZoneDataID,ZoneData in pairs(self.ZoneProbability)do -self:F({ZoneData}) -local ZoneObject=ZoneData[1] -local ZoneProbability=ZoneData[2] -ZoneProbability=ZoneProbability*30/300 -if ZoneObject:IsPointVec2InZone(DetectedObjectVec2)==true then -local Probability=math.random() -if Probability>ZoneProbability then -DetectionAccepted=false -break -end -end -end -end -if DetectionAccepted then -HasDetectedObjects=true -self.DetectedObjects[DetectedObjectName]=self.DetectedObjects[DetectedObjectName]or{} -self.DetectedObjects[DetectedObjectName].Name=DetectedObjectName -if TargetIsDetected and TargetIsDetected==true then -self.DetectedObjects[DetectedObjectName].IsDetected=TargetIsDetected -end -if TargetIsDetected and TargetIsVisible and TargetIsVisible==true then -self.DetectedObjects[DetectedObjectName].IsVisible=TargetIsDetected and TargetIsVisible -end -if TargetIsDetected and not self.DetectedObjects[DetectedObjectName].KnowType then -self.DetectedObjects[DetectedObjectName].KnowType=TargetIsDetected and TargetKnowType -end -self.DetectedObjects[DetectedObjectName].KnowDistance=TargetKnowDistance -self.DetectedObjects[DetectedObjectName].LastTime=(TargetIsDetected and TargetIsVisible==false)and TargetLastTime -self.DetectedObjects[DetectedObjectName].LastPos=(TargetIsDetected and TargetIsVisible==false)and TargetLastPos -self.DetectedObjects[DetectedObjectName].LastVelocity=(TargetIsDetected and TargetIsVisible==false)and TargetLastVelocity -if not self.DetectedObjects[DetectedObjectName].Distance or(Distance and self.DetectedObjects[DetectedObjectName].Distance>Distance)then -self.DetectedObjects[DetectedObjectName].Distance=Distance -end -self.DetectedObjects[DetectedObjectName].DetectionTimeStamp=DetectionTimeStamp -self:F({DetectedObject=self.DetectedObjects[DetectedObjectName]}) -local DetectedUnit=UNIT:FindByName(DetectedObjectName) -DetectedUnits[DetectedObjectName]=DetectedUnit -else -self:F({DetectedObject="No more detection for "..DetectedObjectName}) -if self.DetectedObjects[DetectedObjectName]then -self.DetectedObjects[DetectedObjectName]=nil -end -end -else -self:F("Removing from DetectedObjects: "..DetectionObjectName) -self.DetectedObjects[DetectionObjectName]=nil -end -end -if HasDetectedObjects then -self:__Detected(0.1,DetectedUnits) -end -end -if self.DetectionCount>0 and self.DetectionRun==self.DetectionCount then -for DetectedObjectName,DetectedObject in pairs(self.DetectedObjects)do -if self.DetectedObjects[DetectedObjectName].IsDetected==true and self.DetectedObjects[DetectedObjectName].DetectionTimeStamp+300<=DetectionTimeStamp then -self.DetectedObjects[DetectedObjectName].IsDetected=false -end -end -self:CreateDetectionItems() -for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do -self:UpdateDetectedItemDetection(DetectedItem) -self:CleanDetectionItem(DetectedItem,DetectedItemID) -if DetectedItem then -self:__DetectedItem(0.1,DetectedItem) -end -end -end -end -end -do -function DETECTION_BASE:CleanDetectionItem(DetectedItem,DetectedItemID) -local DetectedSet=DetectedItem.Set -if DetectedSet:Count()==0 then -self:RemoveDetectedItem(DetectedItemID) -end -return self -end -function DETECTION_BASE:ForgetDetectedUnit(UnitName) -local DetectedItems=self:GetDetectedItems() -for DetectedItemIndex,DetectedItem in pairs(DetectedItems)do -local DetectedSet=self:GetDetectedItemSet(DetectedItem) -if DetectedSet then -DetectedSet:RemoveUnitsByName(UnitName) -end -end -return self -end -function DETECTION_BASE:CreateDetectionItems() -self:F("Error, in DETECTION_BASE class...") -return self -end -end -do -function DETECTION_BASE:InitDetectVisual(DetectVisual) -self.DetectVisual=DetectVisual -return self -end -function DETECTION_BASE:InitDetectOptical(DetectOptical) -self:F2() -self.DetectOptical=DetectOptical -return self -end -function DETECTION_BASE:InitDetectRadar(DetectRadar) -self:F2() -self.DetectRadar=DetectRadar -return self -end -function DETECTION_BASE:InitDetectIRST(DetectIRST) -self:F2() -self.DetectIRST=DetectIRST -return self -end -function DETECTION_BASE:InitDetectRWR(DetectRWR) -self:F2() -self.DetectRWR=DetectRWR -return self -end -function DETECTION_BASE:InitDetectDLINK(DetectDLINK) -self:F2() -self.DetectDLINK=DetectDLINK -return self -end -end -do -function DETECTION_BASE:FilterCategories(FilterCategories) -self:F2() -self._.FilterCategories={} -if type(FilterCategories)=="table"then -for CategoryID,Category in pairs(FilterCategories)do -self._.FilterCategories[Category]=Category -end -else -self._.FilterCategories[FilterCategories]=FilterCategories -end -return self -end -end -do -function DETECTION_BASE:SetRefreshTimeInterval(RefreshTimeInterval) -self:F2() -self.RefreshTimeInterval=RefreshTimeInterval -return self -end -end -do -function DETECTION_BASE:SetFriendliesRange(FriendliesRange) -self:F2() -self.FriendliesRange=FriendliesRange -return self -end -end -do -function DETECTION_BASE:SetIntercept(Intercept,InterceptDelay) -self:F2() -self.Intercept=Intercept -self.InterceptDelay=InterceptDelay -return self -end -end -do -function DETECTION_BASE:SetAcceptRange(AcceptRange) -self:F2() -self.AcceptRange=AcceptRange -return self -end -function DETECTION_BASE:SetAcceptZones(AcceptZones) -self:F2() -if type(AcceptZones)=="table"then -if AcceptZones.ClassName and AcceptZones:IsInstanceOf(ZONE_BASE)then -self.AcceptZones={AcceptZones} -else -self.AcceptZones=AcceptZones -end -else -self:F({"AcceptZones must be a list of ZONE_BASE derived objects or one ZONE_BASE derived object",AcceptZones}) -error() -end -return self -end -function DETECTION_BASE:SetRejectZones(RejectZones) -self:F2() -if type(RejectZones)=="table"then -if RejectZones.ClassName and RejectZones:IsInstanceOf(ZONE_BASE)then -self.RejectZones={RejectZones} -else -self.RejectZones=RejectZones -end -else -self:F({"RejectZones must be a list of ZONE_BASE derived objects or one ZONE_BASE derived object",RejectZones}) -error() -end -return self -end -end -do -function DETECTION_BASE:SetDistanceProbability(DistanceProbability) -self:F2() -self.DistanceProbability=DistanceProbability -return self -end -function DETECTION_BASE:SetAlphaAngleProbability(AlphaAngleProbability) -self:F2() -self.AlphaAngleProbability=AlphaAngleProbability -return self -end -function DETECTION_BASE:SetZoneProbability(ZoneArray) -self:F2() -self.ZoneProbability=ZoneArray -return self -end -end -do -function DETECTION_BASE:AcceptChanges(DetectedItem) -DetectedItem.Changed=false -DetectedItem.Changes={} -return self -end -function DETECTION_BASE:AddChangeItem(DetectedItem,ChangeCode,ItemUnitType) -DetectedItem.Changed=true -local ID=DetectedItem.ID -DetectedItem.Changes=DetectedItem.Changes or{} -DetectedItem.Changes[ChangeCode]=DetectedItem.Changes[ChangeCode]or{} -DetectedItem.Changes[ChangeCode].ID=ID -DetectedItem.Changes[ChangeCode].ItemUnitType=ItemUnitType -self:F({"Change on Detected Item:",DetectedItemID=DetectedItem.ID,ChangeCode=ChangeCode,ItemUnitType=ItemUnitType}) -return self -end -function DETECTION_BASE:AddChangeUnit(DetectedItem,ChangeCode,ChangeUnitType) -DetectedItem.Changed=true -local ID=DetectedItem.ID -DetectedItem.Changes=DetectedItem.Changes or{} -DetectedItem.Changes[ChangeCode]=DetectedItem.Changes[ChangeCode]or{} -DetectedItem.Changes[ChangeCode][ChangeUnitType]=DetectedItem.Changes[ChangeCode][ChangeUnitType]or 0 -DetectedItem.Changes[ChangeCode][ChangeUnitType]=DetectedItem.Changes[ChangeCode][ChangeUnitType]+1 -DetectedItem.Changes[ChangeCode].ID=ID -self:F({"Change on Detected Unit:",DetectedItemID=DetectedItem.ID,ChangeCode=ChangeCode,ChangeUnitType=ChangeUnitType}) -return self -end -end -do -function DETECTION_BASE:SetFriendlyPrefixes(FriendlyPrefixes) -self.FriendlyPrefixes=self.FriendlyPrefixes or{} -if type(FriendlyPrefixes)~="table"then -FriendlyPrefixes={FriendlyPrefixes} -end -for PrefixID,Prefix in pairs(FriendlyPrefixes)do -self:F({FriendlyPrefix=Prefix}) -self.FriendlyPrefixes[Prefix]=Prefix -end -return self -end -function DETECTION_BASE:IsFriendliesNearBy(DetectedItem,Category) -return(DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category]~=nil)or false -end -function DETECTION_BASE:GetFriendliesNearBy(DetectedItem,Category) -return DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category] -end -function DETECTION_BASE:IsFriendliesNearIntercept(DetectedItem) -return DetectedItem.FriendliesNearIntercept~=nil or false -end -function DETECTION_BASE:GetFriendliesNearIntercept(DetectedItem) -return DetectedItem.FriendliesNearIntercept -end -function DETECTION_BASE:GetFriendliesDistance(DetectedItem) -return DetectedItem.FriendliesDistance -end -function DETECTION_BASE:IsPlayersNearBy(DetectedItem) -return DetectedItem.PlayersNearBy~=nil -end -function DETECTION_BASE:GetPlayersNearBy(DetectedItem) -return DetectedItem.PlayersNearBy -end -function DETECTION_BASE:ReportFriendliesNearBy(TargetData) -local DetectedItem=TargetData.DetectedItem -local DetectedSet=TargetData.DetectedItem.Set -local DetectedUnit=DetectedSet:GetFirst() -DetectedItem.FriendliesNearBy=nil -if DetectedUnit and DetectedUnit:IsAlive()then -local DetectedUnitCoord=DetectedUnit:GetCoordinate() -local InterceptCoord=TargetData.InterceptCoord or DetectedUnitCoord -local SphereSearch={ -id=world.VolumeType.SPHERE, -params={ -point=InterceptCoord:GetVec3(), -radius=self.FriendliesRange, -} -} -local FindNearByFriendlies=function(FoundDCSUnit,ReportGroupData) -local DetectedItem=ReportGroupData.DetectedItem -local DetectedSet=ReportGroupData.DetectedItem.Set -local DetectedUnit=DetectedSet:GetFirst() -local DetectedUnitCoord=DetectedUnit:GetCoordinate() -local InterceptCoord=ReportGroupData.InterceptCoord or DetectedUnitCoord -local ReportSetGroup=ReportGroupData.ReportSetGroup -local EnemyCoalition=DetectedUnit:GetCoalition() -local FoundUnitCoalition=FoundDCSUnit:getCoalition() -local FoundUnitCategory=FoundDCSUnit:getDesc().category -local FoundUnitName=FoundDCSUnit:getName() -local FoundUnitGroupName=FoundDCSUnit:getGroup():getName() -local EnemyUnitName=DetectedUnit:GetName() -local FoundUnitInReportSetGroup=ReportSetGroup:FindGroup(FoundUnitGroupName)~=nil -if FoundUnitInReportSetGroup==true then -for PrefixID,Prefix in pairs(self.FriendlyPrefixes or{})do -if string.find(FoundUnitName,Prefix:gsub("-","%%-"),1)then -FoundUnitInReportSetGroup=false -break -end -end -end -if FoundUnitCoalition~=EnemyCoalition and FoundUnitInReportSetGroup==false then -local FriendlyUnit=UNIT:Find(FoundDCSUnit) -local FriendlyUnitName=FriendlyUnit:GetName() -local FriendlyUnitCategory=FriendlyUnit:GetDesc().category -DetectedItem.FriendliesNearBy=DetectedItem.FriendliesNearBy or{} -DetectedItem.FriendliesNearBy[FoundUnitCategory]=DetectedItem.FriendliesNearBy[FoundUnitCategory]or{} -DetectedItem.FriendliesNearBy[FoundUnitCategory][FriendlyUnitName]=FriendlyUnit -local Distance=DetectedUnitCoord:Get2DDistance(FriendlyUnit:GetCoordinate()) -DetectedItem.FriendliesDistance=DetectedItem.FriendliesDistance or{} -DetectedItem.FriendliesDistance[Distance]=FriendlyUnit -return true -end -return true -end -world.searchObjects(Object.Category.UNIT,SphereSearch,FindNearByFriendlies,TargetData) -DetectedItem.PlayersNearBy=nil -_DATABASE:ForEachPlayer( -function(PlayerUnitName) -local PlayerUnit=UNIT:FindByName(PlayerUnitName) -if PlayerUnit and PlayerUnit:IsAlive()then -local coord=PlayerUnit:GetCoordinate() -if coord and coord:IsInRadius(DetectedUnitCoord,self.FriendliesRange)then -local PlayerUnitCategory=PlayerUnit:GetDesc().category -if(not self.FriendliesCategory)or(self.FriendliesCategory and(self.FriendliesCategory==PlayerUnitCategory))then -local PlayerUnitName=PlayerUnit:GetName() -DetectedItem.PlayersNearBy=DetectedItem.PlayersNearBy or{} -DetectedItem.PlayersNearBy[PlayerUnitName]=PlayerUnit -DetectedItem.FriendliesNearBy=DetectedItem.FriendliesNearBy or{} -DetectedItem.FriendliesNearBy[PlayerUnitCategory]=DetectedItem.FriendliesNearBy[PlayerUnitCategory]or{} -DetectedItem.FriendliesNearBy[PlayerUnitCategory][PlayerUnitName]=PlayerUnit -local Distance=DetectedUnitCoord:Get2DDistance(PlayerUnit:GetCoordinate()) -DetectedItem.FriendliesDistance=DetectedItem.FriendliesDistance or{} -DetectedItem.FriendliesDistance[Distance]=PlayerUnit -end -end -end -end -) -end -self:F({Friendlies=DetectedItem.FriendliesNearBy,Players=DetectedItem.PlayersNearBy}) -end -end -function DETECTION_BASE:IsDetectedObjectIdentified(DetectedObject) -local DetectedObjectName=DetectedObject.Name -if DetectedObjectName then -local DetectedObjectIdentified=self.DetectedObjectsIdentified[DetectedObjectName]==true -return DetectedObjectIdentified -else -return nil -end -end -function DETECTION_BASE:IdentifyDetectedObject(DetectedObject) -local DetectedObjectName=DetectedObject.Name -self.DetectedObjectsIdentified[DetectedObjectName]=true -end -function DETECTION_BASE:UnIdentifyDetectedObject(DetectedObject) -local DetectedObjectName=DetectedObject.Name -self.DetectedObjectsIdentified[DetectedObjectName]=false -end -function DETECTION_BASE:UnIdentifyAllDetectedObjects() -self.DetectedObjectsIdentified={} -end -function DETECTION_BASE:GetDetectedObject(ObjectName) -self:F2({ObjectName=ObjectName}) -if ObjectName then -local DetectedObject=self.DetectedObjects[ObjectName] -if DetectedObject then -local DetectedUnit=UNIT:FindByName(ObjectName) -if DetectedUnit and DetectedUnit:IsAlive()then -if self:IsDetectedObjectIdentified(DetectedObject)==false then -return DetectedObject -end -end -end -end -return nil -end -function DETECTION_BASE:GetDetectedUnitTypeName(DetectedUnit) -if DetectedUnit and DetectedUnit:IsAlive()then -local DetectedUnitName=DetectedUnit:GetName() -local DetectedObject=self.DetectedObjects[DetectedUnitName] -if DetectedObject then -if DetectedObject.KnowType then -return DetectedUnit:GetTypeName() -else -return"Unknown" -end -else -return"Unknown" -end -else -return"Dead:"..DetectedUnit:GetName() -end -return"Undetected:"..DetectedUnit:GetName() -end -function DETECTION_BASE:AddDetectedItem(ItemPrefix,DetectedItemKey,Set) -local DetectedItem={} -self.DetectedItemCount=self.DetectedItemCount+1 -self.DetectedItemMax=self.DetectedItemMax+1 -DetectedItemKey=DetectedItemKey or self.DetectedItemMax -self.DetectedItems[DetectedItemKey]=DetectedItem -self.DetectedItemsByIndex[DetectedItemKey]=DetectedItem -DetectedItem.Index=DetectedItemKey -DetectedItem.Set=Set or SET_UNIT:New():FilterDeads():FilterCrashes() -DetectedItem.ItemID=ItemPrefix.."."..self.DetectedItemMax -DetectedItem.ID=self.DetectedItemMax -DetectedItem.Removed=false -if self.Locking then -self:LockDetectedItem(DetectedItem) -end -return DetectedItem -end -function DETECTION_BASE:AddDetectedItemZone(ItemPrefix,DetectedItemKey,Set,Zone) -self:F({ItemPrefix,DetectedItemKey,Set,Zone}) -local DetectedItem=self:AddDetectedItem(ItemPrefix,DetectedItemKey,Set) -DetectedItem.Zone=Zone -return DetectedItem -end -function DETECTION_BASE:RemoveDetectedItem(DetectedItemKey) -local DetectedItem=self.DetectedItems[DetectedItemKey] -if DetectedItem then -self.DetectedItemCount=self.DetectedItemCount-1 -local DetectedItemIndex=DetectedItem.Index -self.DetectedItemsByIndex[DetectedItemIndex]=nil -self.DetectedItems[DetectedItemKey]=nil -end -end -function DETECTION_BASE:GetDetectedItems() -return self.DetectedItems -end -function DETECTION_BASE:GetDetectedItemsByIndex() -return self.DetectedItemsByIndex -end -function DETECTION_BASE:GetDetectedItemsCount() -local DetectedCount=self.DetectedItemCount -return DetectedCount -end -function DETECTION_BASE:GetDetectedItemByKey(Key) -self:F({DetectedItems=self.DetectedItems}) -local DetectedItem=self.DetectedItems[Key] -if DetectedItem then -return DetectedItem -end -return nil -end -function DETECTION_BASE:GetDetectedItemByIndex(Index) -self:F({self.DetectedItemsByIndex}) -local DetectedItem=self.DetectedItemsByIndex[Index] -if DetectedItem then -return DetectedItem -end -return nil -end -function DETECTION_BASE:GetDetectedItemID(DetectedItem) -return DetectedItem and DetectedItem.ItemID or"" -end -function DETECTION_BASE:GetDetectedID(Index) -local DetectedItem=self.DetectedItemsByIndex[Index] -if DetectedItem then -return DetectedItem.ID -end -return"" -end -function DETECTION_BASE:GetDetectedItemSet(DetectedItem) -local DetectedSetUnit=DetectedItem and DetectedItem.Set -if DetectedSetUnit then -return DetectedSetUnit -end -return nil -end -function DETECTION_BASE:UpdateDetectedItemDetection(DetectedItem) -local IsDetected=false -for UnitName,UnitData in pairs(DetectedItem.Set:GetSet())do -local DetectedObject=self.DetectedObjects[UnitName] -self:F({UnitName=UnitName,IsDetected=DetectedObject.IsDetected}) -if DetectedObject.IsDetected then -IsDetected=true -break -end -end -self:F({IsDetected=DetectedItem.IsDetected}) -DetectedItem.IsDetected=IsDetected -return IsDetected -end -function DETECTION_BASE:IsDetectedItemDetected(DetectedItem) -return DetectedItem.IsDetected -end -do -function DETECTION_BASE:GetDetectedItemZone(DetectedItem) -local DetectedZone=DetectedItem and DetectedItem.Zone -if DetectedZone then -return DetectedZone -end -local Detected -return nil -end -end -function DETECTION_BASE:LockDetectedItems() -for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do -self:LockDetectedItem(DetectedItem) -end -self.Locking=true -return self -end -function DETECTION_BASE:UnlockDetectedItems() -for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do -self:UnlockDetectedItem(DetectedItem) -end -self.Locking=nil -return self -end -function DETECTION_BASE:IsDetectedItemLocked(DetectedItem) -return self.Locking and DetectedItem.Locked==true -end -function DETECTION_BASE:LockDetectedItem(DetectedItem) -DetectedItem.Locked=true -return self -end -function DETECTION_BASE:UnlockDetectedItem(DetectedItem) -DetectedItem.Locked=nil -return self -end -function DETECTION_BASE:SetDetectedItemCoordinate(DetectedItem,Coordinate,DetectedItemUnit) -self:F({Coordinate=Coordinate}) -if DetectedItem then -if DetectedItemUnit then -DetectedItem.Coordinate=Coordinate -DetectedItem.Coordinate:SetHeading(DetectedItemUnit:GetHeading()) -DetectedItem.Coordinate.y=DetectedItemUnit:GetAltitude() -DetectedItem.Coordinate:SetVelocity(DetectedItemUnit:GetVelocityMPS()) -end -end -end -function DETECTION_BASE:GetDetectedItemCoordinate(DetectedItem) -self:F({DetectedItem=DetectedItem}) -if DetectedItem then -return DetectedItem.Coordinate -end -return nil -end -function DETECTION_BASE:GetDetectedItemCoordinates() -local Coordinates={} -for DetectedItemID,DetectedItem in pairs(self:GetDetectedItems())do -Coordinates[DetectedItem]=self:GetDetectedItemCoordinate(DetectedItem) -end -return Coordinates -end -function DETECTION_BASE:SetDetectedItemThreatLevel(DetectedItem) -local DetectedSet=DetectedItem.Set -if DetectedItem then -DetectedItem.ThreatLevel,DetectedItem.ThreatText=DetectedSet:CalculateThreatLevelA2G() -end -end -function DETECTION_BASE:GetDetectedItemThreatLevel(DetectedItem) -self:F({DetectedItem=DetectedItem}) -if DetectedItem then -self:F({ThreatLevel=DetectedItem.ThreatLevel,ThreatText=DetectedItem.ThreatText}) -return DetectedItem.ThreatLevel or 0,DetectedItem.ThreatText or"" -end -return nil,"" -end -function DETECTION_BASE:DetectedItemReportSummary(DetectedItem,AttackGroup,Settings) -self:F() -return nil -end -function DETECTION_BASE:DetectedReportDetailed(AttackGroup) -self:F() -return nil -end -function DETECTION_BASE:GetDetectionSet() -local DetectionSet=self.DetectionSet -return DetectionSet -end -function DETECTION_BASE:NearestRecce(DetectedItem) -local NearestRecce=nil -local DistanceRecce=1000000000 -for RecceGroupName,RecceGroup in pairs(self.DetectionSet:GetSet())do -if RecceGroup and RecceGroup:IsAlive()then -for RecceUnit,RecceUnit in pairs(RecceGroup:GetUnits())do -if RecceUnit:IsActive()then -local RecceUnitCoord=RecceUnit:GetCoordinate() -local Distance=RecceUnitCoord:Get2DDistance(self:GetDetectedItemCoordinate(DetectedItem)) -if Distance0 then -DetectedItemCoordText=DetectedItemCoordinate:ToStringA2A(AttackGroup,Settings) -else -DetectedItemCoordText=DetectedItemCoordinate:ToStringA2G(AttackGroup,Settings) -end -local ThreatLevelA2G=self:GetDetectedItemThreatLevel(DetectedItem) -local DetectedItemsCount=DetectedSet:Count() -local DetectedItemsTypes=DetectedSet:GetTypeNames() -local Report=REPORT:New() -Report:Add(DetectedItemID..", "..DetectedItemCoordText) -Report:Add(string.format("Threat: [%s%s]",string.rep("■",ThreatLevelA2G),string.rep("□",10-ThreatLevelA2G))) -Report:Add(string.format("Type: %2d of %s",DetectedItemsCount,DetectedItemsTypes)) -return Report -end -return nil -end -function DETECTION_AREAS:DetectedReportDetailed(AttackGroup) -self:F() -local Report=REPORT:New() -for DetectedItemIndex,DetectedItem in pairs(self.DetectedItems)do -local DetectedItem=DetectedItem -local ReportSummary=self:DetectedItemReportSummary(DetectedItem,AttackGroup) -Report:SetTitle("Detected areas:") -Report:Add(ReportSummary:Text()) -end -local ReportText=Report:Text() -return ReportText -end -function DETECTION_AREAS:CalculateIntercept(DetectedItem) -local DetectedCoord=DetectedItem.Coordinate -local DetectedSpeed=DetectedCoord:GetVelocity() -local DetectedHeading=DetectedCoord:GetHeading() -if self.Intercept then -local DetectedSet=DetectedItem.Set -local TranslateDistance=DetectedSpeed*self.InterceptDelay -local InterceptCoord=DetectedCoord:Translate(TranslateDistance,DetectedHeading) -DetectedItem.InterceptCoord=InterceptCoord -else -DetectedItem.InterceptCoord=DetectedCoord -end -end -function DETECTION_AREAS:SmokeDetectedUnits() -self:F2() -self._SmokeDetectedUnits=true -return self -end -function DETECTION_AREAS:FlareDetectedUnits() -self:F2() -self._FlareDetectedUnits=true -return self -end -function DETECTION_AREAS:SmokeDetectedZones() -self:F2() -self._SmokeDetectedZones=true -return self -end -function DETECTION_AREAS:FlareDetectedZones() -self:F2() -self._FlareDetectedZones=true -return self -end -function DETECTION_AREAS:BoundDetectedZones() -self:F2() -self._BoundDetectedZones=true -return self -end -function DETECTION_AREAS:GetChangeText(DetectedItem) -self:F(DetectedItem) -local MT={} -for ChangeCode,ChangeData in pairs(DetectedItem.Changes)do -if ChangeCode=="AA"then -MT[#MT+1]="Detected new area "..ChangeData.ID..". The center target is a "..ChangeData.ItemUnitType.."." -end -if ChangeCode=="RAU"then -MT[#MT+1]="Changed area "..ChangeData.ID..". Removed the center target." -end -if ChangeCode=="AAU"then -MT[#MT+1]="Changed area "..ChangeData.ID..". The new center target is a "..ChangeData.ItemUnitType.."." -end -if ChangeCode=="RA"then -MT[#MT+1]="Removed old area "..ChangeData.ID..". No more targets in this area." -end -if ChangeCode=="AU"then -local MTUT={} -for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do -if ChangeUnitType~="ID"then -MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType -end -end -MT[#MT+1]="Detected for area "..ChangeData.ID.." new target(s) "..table.concat(MTUT,", ").."." -end -if ChangeCode=="RU"then -local MTUT={} -for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do -if ChangeUnitType~="ID"then -MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType -end -end -MT[#MT+1]="Removed for area "..ChangeData.ID.." invisible or destroyed target(s) "..table.concat(MTUT,", ").."." -end -end -return table.concat(MT,"\n") -end -function DETECTION_AREAS:CreateDetectionItems() -self:F("Checking Detected Items for new Detected Units ...") -for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do -local DetectedItem=DetectedItemData -if DetectedItem then -self:T2({"Detected Item ID: ",DetectedItemID}) -local DetectedSet=DetectedItem.Set -local AreaExists=false -self:T3({"Zone Center Unit:",DetectedItem.Zone.ZoneUNIT.UnitName}) -local DetectedZoneObject=self:GetDetectedObject(DetectedItem.Zone.ZoneUNIT.UnitName) -self:T3({"Detected Zone Object:",DetectedItem.Zone:GetName(),DetectedZoneObject}) -if DetectedZoneObject then -AreaExists=true -else -DetectedSet:RemoveUnitsByName(DetectedItem.Zone.ZoneUNIT.UnitName) -self:AddChangeItem(DetectedItem,'RAU',self:GetDetectedUnitTypeName(DetectedItem.Zone.ZoneUNIT)) -for DetectedUnitName,DetectedUnitData in pairs(DetectedSet:GetSet())do -local DetectedUnit=DetectedUnitData -local DetectedObject=self:GetDetectedObject(DetectedUnit.UnitName) -local DetectedUnitTypeName=self:GetDetectedUnitTypeName(DetectedUnit) -if DetectedObject then -self:IdentifyDetectedObject(DetectedObject) -AreaExists=true -DetectedItem.Zone=ZONE_UNIT:New(DetectedUnit:GetName(),DetectedUnit,self.DetectionZoneRange) -self:AddChangeItem(DetectedItem,"AAU",DetectedUnitTypeName) -break -else -DetectedSet:Remove(DetectedUnitName) -self:AddChangeUnit(DetectedItem,"RU",DetectedUnitTypeName) -end -end -end -if AreaExists then -for DetectedUnitName,DetectedUnitData in pairs(DetectedSet:GetSet())do -local DetectedUnit=DetectedUnitData -local DetectedUnitTypeName=self:GetDetectedUnitTypeName(DetectedUnit) -local DetectedObject=nil -if DetectedUnit:IsAlive()then -DetectedObject=self:GetDetectedObject(DetectedUnit:GetName()) -end -if DetectedObject then -if DetectedUnit:IsInZone(DetectedItem.Zone)then -self:IdentifyDetectedObject(DetectedObject) -DetectedSet:AddUnit(DetectedUnit) -else -DetectedSet:Remove(DetectedUnitName) -self:AddChangeUnit(DetectedItem,"RU",DetectedUnitTypeName) -end -else -self:AddChangeUnit(DetectedItem,"RU","destroyed target") -DetectedSet:Remove(DetectedUnitName) -end -end -else -self:RemoveDetectedItem(DetectedItemID) -self:AddChangeItem(DetectedItem,"RA") -end -end -end -for DetectedUnitName,DetectedObjectData in pairs(self.DetectedObjects)do -local DetectedObject=self:GetDetectedObject(DetectedUnitName) -if DetectedObject then -local DetectedUnit=UNIT:FindByName(DetectedUnitName) -local DetectedUnitTypeName=self:GetDetectedUnitTypeName(DetectedUnit) -local AddedToDetectionArea=false -for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do -local DetectedItem=DetectedItemData -if DetectedItem then -local DetectedSet=DetectedItem.Set -if not self:IsDetectedObjectIdentified(DetectedObject)and DetectedUnit:IsInZone(DetectedItem.Zone)then -self:IdentifyDetectedObject(DetectedObject) -DetectedSet:AddUnit(DetectedUnit) -AddedToDetectionArea=true -self:AddChangeUnit(DetectedItem,"AU",DetectedUnitTypeName) -end -end -end -if AddedToDetectionArea==false then -local DetectedItem=self:AddDetectedItemZone("AREA",nil, -SET_UNIT:New():FilterDeads():FilterCrashes(), -ZONE_UNIT:New(DetectedUnitName,DetectedUnit,self.DetectionZoneRange) -) -DetectedItem.Set:AddUnit(DetectedUnit) -self:AddChangeItem(DetectedItem,"AA",DetectedUnitTypeName) -end -end -end -for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do -local DetectedItem=DetectedItemData -local DetectedSet=DetectedItem.Set -local DetectedFirstUnit=DetectedSet:GetFirst() -local DetectedZone=DetectedItem.Zone -local DetectedZoneCoord=DetectedZone:GetCoordinate() -self:SetDetectedItemCoordinate(DetectedItem,DetectedZoneCoord,DetectedFirstUnit) -self:CalculateIntercept(DetectedItem) -local OldFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -self:ReportFriendliesNearBy({DetectedItem=DetectedItem,ReportSetGroup=self.DetectionSet}) -local NewFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -if OldFriendliesNearbyGround~=NewFriendliesNearbyGround then -DetectedItem.Changed=true -end -self:SetDetectedItemThreatLevel(DetectedItem) -self:NearestRecce(DetectedItem) -if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then -DetectedZone.ZoneUNIT:SmokeRed() -end -DetectedSet:ForEachUnit( -function(DetectedUnit) -if DetectedUnit:IsAlive()then -if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then -DetectedUnit:FlareGreen() -end -if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then -DetectedUnit:SmokeGreen() -end -end -end -) -if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then -DetectedZone:FlareZone(SMOKECOLOR.White,30,math.random(0,90)) -end -if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then -DetectedZone:SmokeZone(SMOKECOLOR.White,30) -end -if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then -self.CountryID=DetectedSet:GetFirst():GetCountry() -DetectedZone:BoundZone(12,self.CountryID) -end -end -end -end -do -DETECTION_ZONES={ -ClassName="DETECTION_ZONES", -DetectionZoneRange=nil, -} -function DETECTION_ZONES:New(DetectionSetZone,DetectionCoalition) -local self=BASE:Inherit(self,DETECTION_BASE:New(DetectionSetZone)) -self.DetectionSetZone=DetectionSetZone -self.DetectionCoalition=DetectionCoalition -self._SmokeDetectedUnits=false -self._FlareDetectedUnits=false -self._SmokeDetectedZones=false -self._FlareDetectedZones=false -self._BoundDetectedZones=false -return self -end -function DETECTION_ZONES:CountAliveRecce() -return self.DetectionSetZone:Count() -end -function DETECTION_ZONES:ForEachAliveRecce(IteratorFunction,...) -self:F2(arg) -self.DetectionSetZone:ForEachZone(IteratorFunction,arg) -return self -end -function DETECTION_ZONES:DetectedItemReportSummary(DetectedItem,AttackGroup,Settings) -self:F({DetectedItem=DetectedItem}) -local DetectedItemID=self:GetDetectedItemID(DetectedItem) -if DetectedItem then -local DetectedSet=self:GetDetectedItemSet(DetectedItem) -local ReportSummaryItem -local DetectedZone=self:GetDetectedItemZone(DetectedItem) -local DetectedItemCoordinate=DetectedZone:GetCoordinate() -local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup,Settings) -local ThreatLevelA2G=self:GetDetectedItemThreatLevel(DetectedItem) -local DetectedItemsCount=DetectedSet:Count() -local DetectedItemsTypes=DetectedSet:GetTypeNames() -local Report=REPORT:New() -Report:Add(DetectedItemID..", "..DetectedItemCoordText) -Report:Add(string.format("Threat: [%s]",string.rep("■",ThreatLevelA2G),string.rep("□",10-ThreatLevelA2G))) -Report:Add(string.format("Type: %2d of %s",DetectedItemsCount,DetectedItemsTypes)) -Report:Add(string.format("Detected: %s",DetectedItem.IsDetected and"yes"or"no")) -return Report -end -return nil -end -function DETECTION_ZONES:DetectedReportDetailed(AttackGroup) -self:F() -local Report=REPORT:New() -for DetectedItemIndex,DetectedItem in pairs(self.DetectedItems)do -local DetectedItem=DetectedItem -local ReportSummary=self:DetectedItemReportSummary(DetectedItem,AttackGroup) -Report:SetTitle("Detected areas:") -Report:Add(ReportSummary:Text()) -end -local ReportText=Report:Text() -return ReportText -end -function DETECTION_ZONES:CalculateIntercept(DetectedItem) -local DetectedCoord=DetectedItem.Coordinate -DetectedItem.InterceptCoord=DetectedCoord -end -function DETECTION_ZONES:SmokeDetectedUnits() -self:F2() -self._SmokeDetectedUnits=true -return self -end -function DETECTION_ZONES:FlareDetectedUnits() -self:F2() -self._FlareDetectedUnits=true -return self -end -function DETECTION_ZONES:SmokeDetectedZones() -self:F2() -self._SmokeDetectedZones=true -return self -end -function DETECTION_ZONES:FlareDetectedZones() -self:F2() -self._FlareDetectedZones=true -return self -end -function DETECTION_ZONES:BoundDetectedZones() -self:F2() -self._BoundDetectedZones=true -return self -end -function DETECTION_ZONES:GetChangeText(DetectedItem) -self:F(DetectedItem) -local MT={} -for ChangeCode,ChangeData in pairs(DetectedItem.Changes)do -if ChangeCode=="AA"then -MT[#MT+1]="Detected new area "..ChangeData.ID..". The center target is a "..ChangeData.ItemUnitType.."." -end -if ChangeCode=="RAU"then -MT[#MT+1]="Changed area "..ChangeData.ID..". Removed the center target." -end -if ChangeCode=="AAU"then -MT[#MT+1]="Changed area "..ChangeData.ID..". The new center target is a "..ChangeData.ItemUnitType.."." -end -if ChangeCode=="RA"then -MT[#MT+1]="Removed old area "..ChangeData.ID..". No more targets in this area." -end -if ChangeCode=="AU"then -local MTUT={} -for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do -if ChangeUnitType~="ID"then -MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType -end -end -MT[#MT+1]="Detected for area "..ChangeData.ID.." new target(s) "..table.concat(MTUT,", ").."." -end -if ChangeCode=="RU"then -local MTUT={} -for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do -if ChangeUnitType~="ID"then -MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType -end -end -MT[#MT+1]="Removed for area "..ChangeData.ID.." invisible or destroyed target(s) "..table.concat(MTUT,", ").."." -end -end -return table.concat(MT,"\n") -end -function DETECTION_ZONES:CreateDetectionItems() -self:F("Checking Detected Items for new Detected Units ...") -local DetectedUnits=SET_UNIT:New() -for ZoneName,DetectionZone in pairs(self.DetectionSetZone:GetSet())do -local DetectedItem=self:GetDetectedItemByKey(ZoneName) -if DetectedItem==nil then -DetectedItem=self:AddDetectedItemZone("ZONE",ZoneName,nil,DetectionZone) -end -local DetectedItemSetUnit=self:GetDetectedItemSet(DetectedItem) -DetectionZone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT}) -local ZoneUnits=DetectionZone:GetScannedUnits() -for DCSUnitID,DCSUnit in pairs(ZoneUnits)do -local UnitName=DCSUnit:getName() -local ZoneUnit=UNIT:FindByName(UnitName) -local ZoneUnitCoalition=ZoneUnit:GetCoalition() -if ZoneUnitCoalition==self.DetectionCoalition then -if DetectedItemSetUnit:FindUnit(UnitName)==nil and DetectedUnits:FindUnit(UnitName)==nil then -self:F("Adding "..UnitName) -DetectedItemSetUnit:AddUnit(ZoneUnit) -DetectedUnits:AddUnit(ZoneUnit) -end -end -end -end -for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do -local DetectedItem=DetectedItemData -local DetectedSet=self:GetDetectedItemSet(DetectedItem) -local DetectedFirstUnit=DetectedSet:GetFirst() -local DetectedZone=self:GetDetectedItemZone(DetectedItem) -local DetectedZoneCoord=DetectedZone:GetCoordinate() -self:SetDetectedItemCoordinate(DetectedItem,DetectedZoneCoord,DetectedFirstUnit) -self:CalculateIntercept(DetectedItem) -local OldFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -self:ReportFriendliesNearBy({DetectedItem=DetectedItem,ReportSetGroup=self.DetectionSetGroup}) -local NewFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -if OldFriendliesNearbyGround~=NewFriendliesNearbyGround then -DetectedItem.Changed=true -end -self:SetDetectedItemThreatLevel(DetectedItem) -if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then -DetectedZone:SmokeZone(SMOKECOLOR.Red,30) -end -DetectedSet:ForEachUnit( -function(DetectedUnit) -if DetectedUnit:IsAlive()then -if DETECTION_ZONES._FlareDetectedUnits or self._FlareDetectedUnits then -DetectedUnit:FlareGreen() -end -if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then -DetectedUnit:SmokeGreen() -end -end -end -) -if DETECTION_ZONES._FlareDetectedZones or self._FlareDetectedZones then -DetectedZone:FlareZone(SMOKECOLOR.White,30,math.random(0,90)) -end -if DETECTION_ZONES._SmokeDetectedZones or self._SmokeDetectedZones then -DetectedZone:SmokeZone(SMOKECOLOR.White,30) -end -if DETECTION_ZONES._BoundDetectedZones or self._BoundDetectedZones then -self.CountryID=DetectedSet:GetFirst():GetCountry() -DetectedZone:BoundZone(12,self.CountryID) -end -end -end -function DETECTION_ZONES:onafterDetection(From,Event,To,Detection,DetectionTimeStamp) -self.DetectionRun=self.DetectionRun+1 -if self.DetectionCount>0 and self.DetectionRun==self.DetectionCount then -self:CreateDetectionItems() -for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do -self:UpdateDetectedItemDetection(DetectedItem) -self:CleanDetectionItem(DetectedItem,DetectedItemID) -if DetectedItem then -self:__DetectedItem(0.1,DetectedItem) -end -end -self:__Detect(-self.RefreshTimeInterval) -end -end -function DETECTION_ZONES:UpdateDetectedItemDetection(DetectedItem) -local IsDetected=true -DetectedItem.IsDetected=true -return IsDetected -end -end -do -DESIGNATE={ -ClassName="DESIGNATE", -} -function DESIGNATE:New(CC,Detection,AttackSet,Mission) -local self=BASE:Inherit(self,FSM:New()) -self:F({Detection}) -self:SetStartState("Designating") -self:AddTransition("*","Detect","*") -self:AddTransition("*","LaseOn","Lasing") -self:AddTransition("Lasing","Lasing","Lasing") -self:AddTransition("*","LaseOff","Designate") -self:AddTransition("*","Smoke","*") -self:AddTransition("*","Illuminate","*") -self:AddTransition("*","DoneSmoking","*") -self:AddTransition("*","DoneIlluminating","*") -self:AddTransition("*","Status","*") -self.CC=CC -self.Detection=Detection -self.AttackSet=AttackSet -self.RecceSet=Detection:GetDetectionSet() -self.Recces={} -self.Designating={} -self:SetDesignateName() -self:SetLaseDuration() -self:SetFlashStatusMenu(false) -self:SetFlashDetectionMessages(true) -self:SetMission(Mission) -self:SetLaserCodes({1688,1130,4785,6547,1465,4578}) -self:SetAutoLase(false,false) -self:SetThreatLevelPrioritization(false) -self:SetMaximumDesignations(5) -self:SetMaximumDistanceDesignations(8000) -self:SetMaximumMarkings(2) -self:SetDesignateMenu() -self.LaserCodesUsed={} -self.MenuLaserCodes={} -self.Detection:__Start(2) -self:__Detect(-15) -self.MarkScheduler=SCHEDULER:New(self) -return self -end -function DESIGNATE:SetFlashStatusMenu(FlashMenu) -self.FlashStatusMenu={} -self.AttackSet:ForEachGroupAlive( -function(AttackGroup) -self.FlashStatusMenu[AttackGroup]=FlashMenu -end -) -return self -end -function DESIGNATE:SetFlashDetectionMessages(FlashDetectionMessage) -self.FlashDetectionMessage={} -self.AttackSet:ForEachGroupAlive( -function(AttackGroup) -self.FlashDetectionMessage[AttackGroup]=FlashDetectionMessage -end -) -return self -end -function DESIGNATE:SetMaximumDesignations(MaximumDesignations) -self.MaximumDesignations=MaximumDesignations -return self -end -function DESIGNATE:SetMaximumDistanceGroundDesignation(MaximumDistanceGroundDesignation) -self.MaximumDistanceGroundDesignation=MaximumDistanceGroundDesignation -return self -end -function DESIGNATE:SetMaximumDistanceAirDesignation(MaximumDistanceAirDesignation) -self.MaximumDistanceAirDesignation=MaximumDistanceAirDesignation -return self -end -function DESIGNATE:SetMaximumDistanceDesignations(MaximumDistanceDesignations) -self.MaximumDistanceDesignations=MaximumDistanceDesignations -return self -end -function DESIGNATE:SetMaximumMarkings(MaximumMarkings) -self.MaximumMarkings=MaximumMarkings -return self -end -function DESIGNATE:SetLaserCodes(LaserCodes) -self.LaserCodes=(type(LaserCodes)=="table")and LaserCodes or{LaserCodes} -self:F({LaserCodes=self.LaserCodes}) -self.LaserCodesUsed={} -return self -end -function DESIGNATE:AddMenuLaserCode(LaserCode,MenuText) -self.MenuLaserCodes[LaserCode]=MenuText -self:SetDesignateMenu() -return self -end -function DESIGNATE:RemoveMenuLaserCode(LaserCode) -self.MenuLaserCodes[LaserCode]=nil -self:SetDesignateMenu() -return self -end -function DESIGNATE:SetDesignateName(DesignateName) -self.DesignateName="Designation"..(DesignateName and(" for "..DesignateName)or"") -return self -end -function DESIGNATE:SetLaseDuration(LaseDuration) -self.LaseDuration=LaseDuration or 120 -return self -end -function DESIGNATE:GenerateLaserCodes() -self.LaserCodes={} -local function containsDigit(_number,_numberToFind) -local _thisNumber=_number -local _thisDigit=0 -while _thisNumber~=0 do -_thisDigit=_thisNumber%10 -_thisNumber=math.floor(_thisNumber/10) -if _thisDigit==_numberToFind then -return true -end -end -return false -end -local _code=1111 -local _count=1 -while _code<1777 and _count<30 do -while true do -_code=_code+1 -if not containsDigit(_code,8) -and not containsDigit(_code,9) -and not containsDigit(_code,0)then -self:T(_code) -table.insert(self.LaserCodes,_code) -break -end -end -_count=_count+1 -end -self.LaserCodesUsed={} -return self -end -function DESIGNATE:SetAutoLase(AutoLase,Message) -self.AutoLase=AutoLase or false -if Message then -local AutoLaseOnOff=(self.AutoLase==true)and"On"or"Off" -local CC=self.CC:GetPositionable() -if CC then -CC:MessageToSetGroup(self.DesignateName..": Auto Lase "..AutoLaseOnOff..".",15,self.AttackSet) -end -end -self:CoordinateLase() -self:SetDesignateMenu() -return self -end -function DESIGNATE:SetThreatLevelPrioritization(Prioritize) -self.ThreatLevelPrioritization=Prioritize -return self -end -function DESIGNATE:SetMission(Mission) -self.Mission=Mission -return self -end -function DESIGNATE:onafterDetect() -self:__Detect(-math.random(60)) -self:DesignationScope() -self:CoordinateLase() -self:SendStatus() -self:SetDesignateMenu() -return self -end -function DESIGNATE:DesignationScope() -local DetectedItems=self.Detection:GetDetectedItemsByIndex() -local DetectedItemCount=0 -for DesignateIndex,Designating in pairs(self.Designating)do -local DetectedItem=self.Detection:GetDetectedItemByIndex(DesignateIndex) -if DetectedItem then -local IsDetected=self.Detection:IsDetectedItemDetected(DetectedItem) -self:F({IsDetected=IsDetected}) -if IsDetected==false then -self:F("Removing") -self.Designating[DesignateIndex]=nil -self.AttackSet:ForEachGroupAlive( -function(AttackGroup) -if AttackGroup:IsAlive()==true then -local DetectionText=self.Detection:DetectedItemReportSummary(DetectedItem,AttackGroup):Text(", ") -self.CC:GetPositionable():MessageToGroup("Targets out of LOS\n"..DetectionText,10,AttackGroup,self.DesignateName) -end -end -) -else -DetectedItemCount=DetectedItemCount+1 -end -else -self.Designating[DesignateIndex]=nil -end -end -if DetectedItemCount<5 then -for DesignateIndex,DetectedItem in pairs(DetectedItems)do -local IsDetected=self.Detection:IsDetectedItemDetected(DetectedItem) -if IsDetected==true then -self:F({DistanceRecce=DetectedItem.DistanceRecce}) -if DetectedItem.DistanceRecce<=self.MaximumDistanceDesignations then -if self.Designating[DesignateIndex]==nil then -self.AttackSet:ForEachGroupAlive( -function(AttackGroup) -if self.FlashDetectionMessage[AttackGroup]==true then -local DetectionText=self.Detection:DetectedItemReportSummary(DetectedItem,AttackGroup):Text(", ") -self.CC:GetPositionable():MessageToGroup("Targets detected at \n"..DetectionText,10,AttackGroup,self.DesignateName) -end -end -) -self.Designating[DesignateIndex]="" -break -end -end -end -end -end -return self -end -function DESIGNATE:CoordinateLase() -local DetectedItems=self.Detection:GetDetectedItemsByIndex() -for DesignateIndex,Designating in pairs(self.Designating)do -local DetectedItem=DetectedItems[DesignateIndex] -if DetectedItem then -if self.AutoLase then -self:LaseOn(DesignateIndex,self.LaseDuration) -end -end -end -return self -end -function DESIGNATE:SendStatus(MenuAttackGroup) -self.AttackSet:ForEachGroupAlive( -function(AttackGroup) -if self.FlashStatusMenu[AttackGroup]or(MenuAttackGroup and(AttackGroup:GetName()==MenuAttackGroup:GetName()))then -local DetectedReport=REPORT:New("Targets ready for Designation:") -local DetectedItems=self.Detection:GetDetectedItemsByIndex() -for DesignateIndex,Designating in pairs(self.Designating)do -local DetectedItem=DetectedItems[DesignateIndex] -if DetectedItem then -local Report=self.Detection:DetectedItemReportSummary(DetectedItem,AttackGroup):Text(", ") -DetectedReport:Add(string.rep("-",140)) -DetectedReport:Add(" - "..Report) -if string.find(Designating,"L")then -DetectedReport:Add(" - ".."Lasing Targets") -end -if string.find(Designating,"S")then -DetectedReport:Add(" - ".."Smoking Targets") -end -if string.find(Designating,"I")then -DetectedReport:Add(" - ".."Illuminating Area") -end -end -end -local CC=self.CC:GetPositionable() -CC:MessageTypeToGroup(DetectedReport:Text("\n"),MESSAGE.Type.Information,AttackGroup,self.DesignateName) -local DesignationReport=REPORT:New("Marking Targets:") -self.RecceSet:ForEachGroupAlive( -function(RecceGroup) -local RecceUnits=RecceGroup:GetUnits() -for UnitID,RecceData in pairs(RecceUnits)do -local Recce=RecceData -if Recce:IsLasing()then -DesignationReport:Add(" - "..Recce:GetMessageText("Marking "..Recce:GetSpot().Target:GetTypeName().." with laser "..Recce:GetSpot().LaserCode..".")) -end -end -end -) -CC:MessageTypeToGroup(DesignationReport:Text(),MESSAGE.Type.Information,AttackGroup,self.DesignateName) -end -end -) -return self -end -function DESIGNATE:SetMenu(AttackGroup) -self.MenuDesignate=self.MenuDesignate or{} -local MissionMenu=nil -if self.Mission then -MissionMenu=self.Mission:GetMenu(AttackGroup) -end -local MenuTime=timer.getTime() -self.MenuDesignate[AttackGroup]=MENU_GROUP_DELAYED:New(AttackGroup,self.DesignateName,MissionMenu):SetTime(MenuTime):SetTag(self.DesignateName) -local MenuDesignate=self.MenuDesignate[AttackGroup] -if self.AutoLase then -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Auto Lase Off",MenuDesignate,self.MenuAutoLase,self,false):SetTime(MenuTime):SetTag(self.DesignateName) -else -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Auto Lase On",MenuDesignate,self.MenuAutoLase,self,true):SetTime(MenuTime):SetTag(self.DesignateName) -end -local StatusMenu=MENU_GROUP_DELAYED:New(AttackGroup,"Status",MenuDesignate):SetTime(MenuTime):SetTag(self.DesignateName) -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Report Status",StatusMenu,self.MenuStatus,self,AttackGroup):SetTime(MenuTime):SetTag(self.DesignateName) -if self.FlashStatusMenu[AttackGroup]then -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Flash Status Report Off",StatusMenu,self.MenuFlashStatus,self,AttackGroup,false):SetTime(MenuTime):SetTag(self.DesignateName) -else -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Flash Status Report On",StatusMenu,self.MenuFlashStatus,self,AttackGroup,true):SetTime(MenuTime):SetTag(self.DesignateName) -end -local DesignateCount=0 -for DesignateIndex,Designating in pairs(self.Designating)do -local DetectedItem=self.Detection:GetDetectedItemByIndex(DesignateIndex) -if DetectedItem then -local Coord=self.Detection:GetDetectedItemCoordinate(DetectedItem) -local ID=self.Detection:GetDetectedItemID(DetectedItem) -local MenuText=ID -if DetectedItem.DesignateMenuName then -MenuText=string.format("(%3s) %s",Designating,DetectedItem.DesignateMenuName) -else -MenuText=string.format("(%3s) %s",Designating,MenuText) -end -local DetectedMenu=MENU_GROUP_DELAYED:New(AttackGroup,MenuText,MenuDesignate):SetTime(MenuTime):SetTag(self.DesignateName) -if string.find(Designating,"L",1,true)==nil then -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Search other target",DetectedMenu,self.MenuForget,self,DesignateIndex):SetTime(MenuTime):SetTag(self.DesignateName) -for LaserCode,MenuText in pairs(self.MenuLaserCodes)do -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,string.format(MenuText,LaserCode),DetectedMenu,self.MenuLaseCode,self,DesignateIndex,self.LaseDuration,LaserCode):SetTime(MenuTime):SetTag(self.DesignateName) -end -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Lase with random laser code(s)",DetectedMenu,self.MenuLaseOn,self,DesignateIndex,self.LaseDuration):SetTime(MenuTime):SetTag(self.DesignateName) -else -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Stop lasing",DetectedMenu,self.MenuLaseOff,self,DesignateIndex):SetTime(MenuTime):SetTag(self.DesignateName) -end -if string.find(Designating,"S",1,true)==nil then -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Smoke red",DetectedMenu,self.MenuSmoke,self,DesignateIndex,SMOKECOLOR.Red):SetTime(MenuTime):SetTag(self.DesignateName) -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Smoke blue",DetectedMenu,self.MenuSmoke,self,DesignateIndex,SMOKECOLOR.Blue):SetTime(MenuTime):SetTag(self.DesignateName) -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Smoke green",DetectedMenu,self.MenuSmoke,self,DesignateIndex,SMOKECOLOR.Green):SetTime(MenuTime):SetTag(self.DesignateName) -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Smoke white",DetectedMenu,self.MenuSmoke,self,DesignateIndex,SMOKECOLOR.White):SetTime(MenuTime):SetTag(self.DesignateName) -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Smoke orange",DetectedMenu,self.MenuSmoke,self,DesignateIndex,SMOKECOLOR.Orange):SetTime(MenuTime):SetTag(self.DesignateName) -end -if string.find(Designating,"I",1,true)==nil then -MENU_GROUP_COMMAND_DELAYED:New(AttackGroup,"Illuminate",DetectedMenu,self.MenuIlluminate,self,DesignateIndex):SetTime(MenuTime):SetTag(self.DesignateName) -end -end -DesignateCount=DesignateCount+1 -if DesignateCount>10 then -break -end -end -MenuDesignate:Remove(MenuTime,self.DesignateName) -MenuDesignate:Set() -end -function DESIGNATE:SetDesignateMenu() -self.AttackSet:Flush(self) -local Delay=1 -self.AttackSet:ForEachGroupAlive( -function(AttackGroup) -self:ScheduleOnce(Delay,self.SetMenu,self,AttackGroup) -Delay=Delay+1 -end -) -return self -end -function DESIGNATE:MenuStatus(AttackGroup) -self:F("Status") -self:SendStatus(AttackGroup) -end -function DESIGNATE:MenuFlashStatus(AttackGroup,Flash) -self:F("Flash Status") -self.FlashStatusMenu[AttackGroup]=Flash -self:SetDesignateMenu() -end -function DESIGNATE:MenuForget(Index) -self:F("Forget") -self.Designating[Index]="" -self:SetDesignateMenu() -end -function DESIGNATE:MenuAutoLase(AutoLase) -self:F("AutoLase") -self:SetAutoLase(AutoLase,true) -end -function DESIGNATE:MenuSmoke(Index,Color) -self:F("Designate through Smoke") -if string.find(self.Designating[Index],"S")==nil then -self.Designating[Index]=self.Designating[Index].."S" -end -self:Smoke(Index,Color) -self:SetDesignateMenu() -end -function DESIGNATE:MenuIlluminate(Index) -self:F("Designate through Illumination") -if string.find(self.Designating[Index],"I",1,true)==nil then -self.Designating[Index]=self.Designating[Index].."I" -end -self:__Illuminate(1,Index) -self:SetDesignateMenu() -end -function DESIGNATE:MenuLaseOn(Index,Duration) -self:F("Designate through Lase") -self:__LaseOn(1,Index,Duration) -self:SetDesignateMenu() -end -function DESIGNATE:MenuLaseCode(Index,Duration,LaserCode) -self:F("Designate through Lase using "..LaserCode) -self:__LaseOn(1,Index,Duration,LaserCode) -self:SetDesignateMenu() -end -function DESIGNATE:MenuLaseOff(Index,Duration) -self:F("Lasing off") -self.Designating[Index]=string.gsub(self.Designating[Index],"L","") -self:__LaseOff(1,Index) -self:SetDesignateMenu() -end -function DESIGNATE:onafterLaseOn(From,Event,To,Index,Duration,LaserCode) -if string.find(self.Designating[Index],"L",1,true)==nil then -self.Designating[Index]=self.Designating[Index].."L" -self.LaseStart=timer.getTime() -self.LaseDuration=Duration -self:Lasing(Index,Duration,LaserCode) -end -end -function DESIGNATE:onafterLasing(From,Event,To,Index,Duration,LaserCodeRequested) -local DetectedItem=self.Detection:GetDetectedItemByIndex(Index) -local TargetSetUnit=self.Detection:GetDetectedItemSet(DetectedItem) -local MarkingCount=0 -local MarkedTypes={} -local ReportTypes=REPORT:New() -local ReportLaserCodes=REPORT:New() -TargetSetUnit:Flush(self) -for TargetUnit,RecceData in pairs(self.Recces)do -local Recce=RecceData -self:F({TargetUnit=TargetUnit,Recce=Recce:GetName()}) -if not Recce:IsLasing()then -local LaserCode=Recce:GetLaserCode() -self:F({ClearingLaserCode=LaserCode}) -self.LaserCodesUsed[LaserCode]=nil -self.Recces[TargetUnit]=nil -end -end -if LaserCodeRequested then -for TargetUnit,RecceData in pairs(self.Recces)do -local Recce=RecceData -self:F({TargetUnit=TargetUnit,Recce=Recce:GetName()}) -if Recce:IsLasing()then -Recce:LaseOff() -local LaserCode=Recce:GetLaserCode() -self:F({ClearingLaserCode=LaserCode}) -self.LaserCodesUsed[LaserCode]=nil -self.Recces[TargetUnit]=nil -break -end -end -end -if self.AutoLase or(not self.AutoLase and(self.LaseStart+Duration>=timer.getTime()))then -TargetSetUnit:ForEachUnitPerThreatLevel(10,0, -function(TargetUnit) -self:F({TargetUnit=TargetUnit:GetName()}) -if MarkingCount0 and self.takeoff~=RAT.wp.air then -self.takeoff=RAT.wp.air -self:E(RAT.id..string.format("ERROR: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start for RAT group %s!",self.alias)) -end -if self.Ndeparture_Airports==0 and self.Ndeparture_Zone==0 then -self.random_departure=true -local text=string.format("No airports or zones found given in SetDeparture(). Enabling random departure airports for RAT group %s!",self.alias) -self:E(RAT.id.."ERROR: "..text) -MESSAGE:New(text,30):ToAll() -end -end -if not self.random_destination then -for _,name in pairs(self.destination_ports)do -if self:_AirportExists(name)then -self.Ndestination_Airports=self.Ndestination_Airports+1 -elseif self:_ZoneExists(name)then -self.Ndestination_Zones=self.Ndestination_Zones+1 -end -end -if self.Ndestination_Zones>0 and self.landing~=RAT.wp.air and not self.returnzone then -self.landing=RAT.wp.air -self.destinationzone=true -self:E(RAT.id.."ERROR: At least one zone defined as destination and landing is NOT set to air. Enabling destination zone!") -end -if self.Ndestination_Airports==0 and self.Ndestination_Zones==0 then -self.random_destination=true -local text="No airports or zones found given in SetDestination(). Enabling random destination airports!" -self:E(RAT.id.."ERROR: "..text) -MESSAGE:New(text,30):ToAll() -end -end -if self.destinationzone and self.returnzone then -self:E(RAT.id.."ERROR: Destination zone _and_ return to zone not possible! Disabling return to zone.") -self.returnzone=false -end -if self.returnzone and self.takeoff==RAT.wp.air then -self.landing=RAT.wp.air -end -if self.FLminuser then -self.FLminuser=math.min(self.FLminuser,self.aircraft.ceiling) -end -if self.FLmaxuser then -self.FLmaxuser=math.min(self.FLmaxuser,self.aircraft.ceiling) -end -if self.FLcruise then -self.FLcruise=math.min(self.FLcruise,self.aircraft.ceiling) -end -if self.FLminuser and self.FLmaxuser then -if self.FLminuser>self.FLmaxuser then -local min=self.FLminuser -local max=self.FLmaxuser -self.FLminuser=max -self.FLmaxuser=min -end -end -if self.FLminuser and self.FLcruiseself.FLmaxuser then -self.FLcruise=self.FLmaxuser -end -if self.uncontrolled then -self.takeoff=RAT.wp.cold -end -end -function RAT:SetCoalition(friendly) -self:F2(friendly) -if friendly:lower()=="sameonly"then -self.friendly=RAT.coal.sameonly -elseif friendly:lower()=="neutral"then -self.friendly=RAT.coal.neutral -else -self.friendly=RAT.coal.same -end -return self -end -function RAT:SetCoalitionAircraft(color) -self:F2(color) -if color:lower()=="blue"then -self.coalition=coalition.side.BLUE -if not self.country then -self.country=country.id.USA -end -elseif color:lower()=="red"then -self.coalition=coalition.side.RED -if not self.country then -self.country=country.id.RUSSIA -end -elseif color:lower()=="neutral"then -self.coalition=coalition.side.NEUTRAL -if not self.country then -self.country=country.id.SWITZERLAND -end -end -return self -end -function RAT:SetCountry(id) -self:F2(id) -self.country=id -return self -end -function RAT:SetTerminalType(termtype) -self:F2(termtype) -self.termtype=termtype -return self -end -function RAT:SetParkingScanRadius(radius) -self:F2(radius) -self.parkingscanradius=radius or 50 -return self -end -function RAT:SetParkingScanSceneryON() -self:F2() -self.parkingscanscenery=true -return self -end -function RAT:SetParkingScanSceneryOFF() -self:F2() -self.parkingscanscenery=false -return self -end -function RAT:SetParkingSpotSafeON() -self:F2() -self.parkingverysafe=true -return self -end -function RAT:SetParkingSpotSafeOFF() -self:F2() -self.parkingverysafe=false -return self -end -function RAT:SetDespawnAirOFF() -self.despawnair=false -return self -end -function RAT:SetTakeoff(type) -self:F2(type) -local _Type -if type:lower()=="takeoff-cold"or type:lower()=="cold"then -_Type=RAT.wp.cold -elseif type:lower()=="takeoff-hot"or type:lower()=="hot"then -_Type=RAT.wp.hot -elseif type:lower()=="takeoff-runway"or type:lower()=="runway"then -_Type=RAT.wp.runway -elseif type:lower()=="air"then -_Type=RAT.wp.air -else -_Type=RAT.wp.coldorhot -end -self.takeoff=_Type -return self -end -function RAT:SetTakeoffCold() -self.takeoff=RAT.wp.cold -return self -end -function RAT:SetTakeoffHot() -self.takeoff=RAT.wp.hot -return self -end -function RAT:SetTakeoffRunway() -self.takeoff=RAT.wp.runway -return self -end -function RAT:SetTakeoffColdOrHot() -self.takeoff=RAT.wp.coldorhot -return self -end -function RAT:SetTakeoffAir() -self.takeoff=RAT.wp.air -return self -end -function RAT:SetDeparture(departurenames) -self:F2(departurenames) -self.random_departure=false -local names -if type(departurenames)=="table"then -names=departurenames -elseif type(departurenames)=="string"then -names={departurenames} -else -self:E(RAT.id.."ERROR: Input parameter must be a string or a table in SetDeparture()!") -end -for _,name in pairs(names)do -if self:_AirportExists(name)then -table.insert(self.departure_ports,name) -elseif self:_ZoneExists(name)then -table.insert(self.departure_ports,name) -else -self:E(RAT.id.."ERROR: No departure airport or zone found with name "..name) -end -end -return self -end -function RAT:SetDestination(destinationnames) -self:F2(destinationnames) -self.random_destination=false -local names -if type(destinationnames)=="table"then -names=destinationnames -elseif type(destinationnames)=="string"then -names={destinationnames} -else -self:E(RAT.id.."ERROR: Input parameter must be a string or a table in SetDestination()!") -end -for _,name in pairs(names)do -if self:_AirportExists(name)then -table.insert(self.destination_ports,name) -elseif self:_ZoneExists(name)then -table.insert(self.destination_ports,name) -else -self:E(RAT.id.."ERROR: No destination airport or zone found with name "..name) -end -end -return self -end -function RAT:DestinationZone() -self:F2() -self.destinationzone=true -self.landing=RAT.wp.air -return self -end -function RAT:ReturnZone() -self:F2() -self.returnzone=true -return self -end -function RAT:SetDestinationsFromZone(zone) -self:F2(zone) -self.random_destination=false -self.destination_Azone=zone -return self -end -function RAT:SetDeparturesFromZone(zone) -self:F2(zone) -self.random_departure=false -self.departure_Azone=zone -return self -end -function RAT:AddFriendlyAirportsToDepartures() -self:F2() -self.addfriendlydepartures=true -return self -end -function RAT:AddFriendlyAirportsToDestinations() -self:F2() -self.addfriendlydestinations=true -return self -end -function RAT:ExcludedAirports(ports) -self:F2(ports) -if type(ports)=="string"then -self.excluded_ports={ports} -else -self.excluded_ports=ports -end -return self -end -function RAT:SetAISkill(skill) -self:F2(skill) -if skill:lower()=="average"then -self.skill="Average" -elseif skill:lower()=="good"then -self.skill="Good" -elseif skill:lower()=="excellent"then -self.skill="Excellent" -elseif skill:lower()=="random"then -self.skill="Random" -else -self.skill="High" -end -return self -end -function RAT:Livery(skins) -self:F2(skins) -if type(skins)=="string"then -self.livery={skins} -else -self.livery=skins -end -return self -end -function RAT:ChangeAircraft(actype) -self:F2(actype) -self.actype=actype -return self -end -function RAT:ContinueJourney() -self:F2() -self.continuejourney=true -self.commute=false -return self -end -function RAT:Commute(starshape) -self:F2() -self.commute=true -self.continuejourney=false -if starshape then -self.starshape=starshape -else -self.starshape=false -end -return self -end -function RAT:SetSpawnDelay(delay) -self:F2(delay) -delay=delay or 5 -self.spawndelay=math.max(0.5,delay) -return self -end -function RAT:SetSpawnInterval(interval) -self:F2(interval) -interval=interval or 5 -self.spawninterval=math.max(0.5,interval) -return self -end -function RAT:RespawnAfterLanding(delay) -self:F2(delay) -delay=delay or 180 -self.respawn_at_landing=true -delay=math.max(1.0,delay) -self.respawn_delay=delay -return self -end -function RAT:SetRespawnDelay(delay) -self:F2(delay) -delay=delay or 1.0 -delay=math.max(1.0,delay) -self.respawn_delay=delay -return self -end -function RAT:NoRespawn() -self:F2() -self.norespawn=true -return self -end -function RAT:SetMaxRespawnTriedWhenSpawnedOnRunway(n) -self:F2(n) -n=n or 3 -self.onrunwaymaxretry=n -return self -end -function RAT:RespawnAfterTakeoff() -self:F2() -self.respawn_after_takeoff=true -return self -end -function RAT:RespawnAfterCrashON() -self:F2() -self.respawn_after_crash=true -return self -end -function RAT:RespawnAfterCrashOFF() -self:F2() -self.respawn_after_crash=false -return self -end -function RAT:RespawnInAirAllowed() -self:F2() -self.respawn_inair=true -return self -end -function RAT:RespawnInAirNotAllowed() -self:F2() -self.respawn_inair=false -return self -end -function RAT:CheckOnRunway(switch,distance) -self:F2(switch) -if switch==nil then -switch=true -end -self.checkonrunway=switch -self.onrunwayradius=distance or 75 -return self -end -function RAT:CheckOnTop(switch,radius) -self:F2(switch) -if switch==nil then -switch=true -end -self.checkontop=switch -self.ontopradius=radius or 2 -return self -end -function RAT:ParkingSpotDB(switch) -self:E("RAT ParkingSpotDB function is obsolete and will be removed soon!") -return self -end -function RAT:RadioON() -self:F2() -self.radio=true -return self -end -function RAT:RadioOFF() -self:F2() -self.radio=false -return self -end -function RAT:RadioFrequency(frequency) -self:F2(frequency) -self.frequency=frequency -return self -end -function RAT:RadioModulation(modulation) -self:F2(modulation) -if modulation=="AM"then -self.modulation=radio.modulation.AM -elseif modulation=="FM"then -self.modulation=radio.modulation.FM -else -self.modulation=radio.modulation.AM -end -return self -end -function RAT:RadioMenuON() -self:F2() -self.f10menu=true -return self -end -function RAT:RadioMenuOFF() -self:F2() -self.f10menu=false -return self -end -function RAT:Invisible() -self:F2() -self.invisible=true -return self -end -function RAT:SetEPLRS(switch) -if switch==nil or switch==true then -self.eplrs=true -else -self.eplrs=false -end -return self -end -function RAT:Immortal() -self:F2() -self.immortal=true -return self -end -function RAT:Uncontrolled() -self:F2() -self.uncontrolled=true -return self -end -function RAT:ActivateUncontrolled(maxactivated,delay,delta,frand) -self:F2({max=maxactivated,delay=delay,delta=delta,rand=frand}) -self.activate_uncontrolled=true -self.activate_max=maxactivated or 1 -self.activate_delay=delay or 1 -self.activate_delta=delta or 1 -self.activate_frand=frand or 0 -self.activate_delay=math.max(self.activate_delay,1) -self.activate_delta=math.max(self.activate_delta,0) -self.activate_frand=math.max(self.activate_frand,0) -self.activate_frand=math.min(self.activate_frand,1) -return self -end -function RAT:TimeDestroyInactive(time) -self:F2(time) -time=time or self.Tinactive -time=math.max(time,60) -self.Tinactive=time -return self -end -function RAT:SetMaxCruiseSpeed(speed) -self:F2(speed) -self.Vcruisemax=speed/3.6 -return self -end -function RAT:SetClimbRate(rate) -self:F2(rate) -rate=rate or self.Vclimb -rate=math.max(rate,100) -rate=math.min(rate,15000) -self.Vclimb=rate -return self -end -function RAT:SetDescentAngle(angle) -self:F2(angle) -angle=angle or self.AlphaDescent -angle=math.max(angle,0.5) -angle=math.min(angle,50) -self.AlphaDescent=angle -return self -end -function RAT:SetROE(roe) -self:F2(roe) -if roe=="return"then -self.roe=RAT.ROE.returnfire -elseif roe=="free"then -self.roe=RAT.ROE.weaponfree -else -self.roe=RAT.ROE.weaponhold -end -return self -end -function RAT:SetROT(rot) -self:F2(rot) -if rot=="passive"then -self.rot=RAT.ROT.passive -elseif rot=="evade"then -self.rot=RAT.ROT.evade -else -self.rot=RAT.ROT.noreaction -end -return self -end -function RAT:MenuName(name) -self:F2(name) -self.SubMenuName=tostring(name) -return self -end -function RAT:EnableATC(switch) -self:F2(switch) -if switch==nil then -switch=true -end -self.ATCswitch=switch -return self -end -function RAT:ATC_Messages(switch) -self:F2(switch) -if switch==nil then -switch=true -end -RAT.ATC.messages=switch -return self -end -function RAT:ATC_Clearance(n) -self:F2(n) -RAT.ATC.Nclearance=n or 2 -return self -end -function RAT:ATC_Delay(time) -self:F2(time) -RAT.ATC.delay=time or 240 -return self -end -function RAT:SetMinDistance(dist) -self:F2(dist) -self.mindist=math.max(100,dist*1000) -return self -end -function RAT:SetMaxDistance(dist) -self:F2(dist) -self.maxdist=dist*1000 -return self -end -function RAT:_Debug(switch) -self:F2(switch) -if switch==nil then -switch=true -end -self.Debug=switch -return self -end -function RAT:Debugmode() -self:F2() -self.Debug=true -return self -end -function RAT:StatusReports(switch) -self:F2(switch) -if switch==nil then -switch=true -end -self.reportstatus=switch -return self -end -function RAT:PlaceMarkers(switch) -self:F2(switch) -if switch==nil then -switch=true -end -self.placemarkers=switch -return self -end -function RAT:SetFL(FL) -self:F2(FL) -FL=FL or self.FLcruise -FL=math.max(FL,0) -self.FLuser=FL*RAT.unit.FL2m -return self -end -function RAT:SetFLmax(FL) -self:F2(FL) -self.FLmaxuser=FL*RAT.unit.FL2m -return self -end -function RAT:SetMaxCruiseAltitude(alt) -self:F2(alt) -self.FLmaxuser=alt -return self -end -function RAT:SetFLmin(FL) -self:F2(FL) -self.FLminuser=FL*RAT.unit.FL2m -return self -end -function RAT:SetMinCruiseAltitude(alt) -self:F2(alt) -self.FLminuser=alt -return self -end -function RAT:SetFLcruise(FL) -self:F2(FL) -self.FLcruise=FL*RAT.unit.FL2m -return self -end -function RAT:SetCruiseAltitude(alt) -self:F2(alt) -self.FLcruise=alt -return self -end -function RAT:SetOnboardNum(tailnumprefix,zero) -self:F2({tailnumprefix=tailnumprefix,zero=zero}) -self.onboardnum=tailnumprefix -if zero~=nil then -self.onboardnum0=zero -end -return self -end -function RAT:_InitAircraft(DCSgroup) -self:F2(DCSgroup) -local DCSunit=DCSgroup:getUnit(1) -local DCSdesc=DCSunit:getDesc() -local DCScategory=DCSgroup:getCategory() -local DCStype=DCSunit:getTypeName() -if DCScategory==Group.Category.AIRPLANE then -self.category=RAT.cat.plane -elseif DCScategory==Group.Category.HELICOPTER then -self.category=RAT.cat.heli -else -self.category="other" -self:E(RAT.id.."ERROR: Group of RAT is neither airplane nor helicopter!") -end -self.aircraft.type=DCStype -self.aircraft.fuel=DCSunit:getFuel() -self.aircraft.Rmax=DCSdesc.range*RAT.unit.nm2m -self.aircraft.Reff=self.aircraft.Rmax*self.aircraft.fuel*0.95 -self.aircraft.Vmax=DCSdesc.speedMax -self.aircraft.Vymax=DCSdesc.VyMax -self.aircraft.ceiling=DCSdesc.Hmax -self.aircraft.length=DCSdesc.box.max.x -self.aircraft.height=DCSdesc.box.max.y -self.aircraft.width=DCSdesc.box.max.z -self.aircraft.box=math.max(self.aircraft.length,self.aircraft.width) -local text=string.format("\n******************************************************\n") -text=text..string.format("Aircraft parameters:\n") -text=text..string.format("Template group = %s\n",self.SpawnTemplatePrefix) -text=text..string.format("Alias = %s\n",self.alias) -text=text..string.format("Category = %s\n",self.category) -text=text..string.format("Type = %s\n",self.aircraft.type) -text=text..string.format("Length (x) = %6.1f m\n",self.aircraft.length) -text=text..string.format("Width (z) = %6.1f m\n",self.aircraft.width) -text=text..string.format("Height (y) = %6.1f m\n",self.aircraft.height) -text=text..string.format("Max air speed = %6.1f m/s\n",self.aircraft.Vmax) -text=text..string.format("Max climb speed = %6.1f m/s\n",self.aircraft.Vymax) -text=text..string.format("Initial Fuel = %6.1f\n",self.aircraft.fuel*100) -text=text..string.format("Max range = %6.1f km\n",self.aircraft.Rmax/1000) -text=text..string.format("Eff range = %6.1f km (with 95 percent initial fuel amount)\n",self.aircraft.Reff/1000) -text=text..string.format("Ceiling = %6.1f km = FL%3.0f\n",self.aircraft.ceiling/1000,self.aircraft.ceiling/RAT.unit.FL2m) -text=text..string.format("******************************************************\n") -self:T(RAT.id..text) -end -function RAT:_SpawnWithRoute(_departure,_destination,_takeoff,_landing,_livery,_waypoint,_lastpos,_nrespawn,parkingdata) -self:F({rat=RAT.id,departure=_departure,destination=_destination,takeoff=_takeoff,landing=_landing,livery=_livery,waypoint=_waypoint,lastpos=_lastpos,nrespawn=_nrespawn}) -local takeoff=self.takeoff -local landing=self.landing -if _takeoff then -takeoff=_takeoff -end -if _landing then -landing=_landing -end -if takeoff==RAT.wp.coldorhot then -local temp={RAT.wp.cold,RAT.wp.hot} -takeoff=temp[math.random(2)] -end -local nrespawn=0 -if _nrespawn then -nrespawn=_nrespawn -end -local departure,destination,waypoints,WPholding,WPfinal=self:_SetRoute(takeoff,landing,_departure,_destination,_waypoint) -if not(departure and destination and waypoints)then -return nil -end -local livery -if _livery then -livery=_livery -elseif self.livery then -livery=self.livery[math.random(#self.livery)] -local text=string.format("Chosen livery for group %s: %s",self:_AnticipatedGroupName(),livery) -self:T(RAT.id..text) -else -livery=nil -end -local successful=self:_ModifySpawnTemplate(waypoints,livery,_lastpos,departure,takeoff,parkingdata) -if not successful then -return nil -end -local group=self:SpawnWithIndex(self.SpawnIndex) -self.alive=self.alive+1 -self:T(RAT.id..string.format("Alive groups counter now = %d.",self.alive)) -if self.ATCswitch and landing==RAT.wp.landing then -if self.returnzone then -self:_ATCAddFlight(group:GetName(),departure:GetName()) -else -self:_ATCAddFlight(group:GetName(),destination:GetName()) -end -end -if self.placemarkers then -self:_PlaceMarkers(waypoints,self.SpawnIndex) -end -if self.invisible then -self:_CommandInvisible(group,true) -end -if self.immortal then -self:_CommandImmortal(group,true) -end -if self.eplrs then -group:CommandEPLRS(true,1) -end -self:_SetROE(group,self.roe) -self:_SetROT(group,self.rot) -self.ratcraft[self.SpawnIndex]={} -self.ratcraft[self.SpawnIndex]["group"]=group -self.ratcraft[self.SpawnIndex]["destination"]=destination -self.ratcraft[self.SpawnIndex]["departure"]=departure -self.ratcraft[self.SpawnIndex]["waypoints"]=waypoints -self.ratcraft[self.SpawnIndex]["airborne"]=group:InAir() -self.ratcraft[self.SpawnIndex]["nunits"]=group:GetInitialSize() -if group:InAir()then -self.ratcraft[self.SpawnIndex]["Tground"]=nil -self.ratcraft[self.SpawnIndex]["Pground"]=nil -self.ratcraft[self.SpawnIndex]["Uground"]=nil -self.ratcraft[self.SpawnIndex]["Tlastcheck"]=nil -else -self.ratcraft[self.SpawnIndex]["Tground"]=timer.getTime() -self.ratcraft[self.SpawnIndex]["Pground"]=group:GetCoordinate() -self.ratcraft[self.SpawnIndex]["Uground"]={} -for _,_unit in pairs(group:GetUnits())do -local _unitname=_unit:GetName() -self.ratcraft[self.SpawnIndex]["Uground"][_unitname]=_unit:GetCoordinate() -end -self.ratcraft[self.SpawnIndex]["Tlastcheck"]=timer.getTime() -end -self.ratcraft[self.SpawnIndex]["P0"]=group:GetCoordinate() -self.ratcraft[self.SpawnIndex]["Pnow"]=group:GetCoordinate() -self.ratcraft[self.SpawnIndex]["Distance"]=0 -self.ratcraft[self.SpawnIndex].takeoff=takeoff -self.ratcraft[self.SpawnIndex].landing=landing -self.ratcraft[self.SpawnIndex].wpholding=WPholding -self.ratcraft[self.SpawnIndex].wpfinal=WPfinal -self.ratcraft[self.SpawnIndex].active=not self.uncontrolled -self.ratcraft[self.SpawnIndex]["status"]=RAT.status.Spawned -self.ratcraft[self.SpawnIndex].livery=livery -self.ratcraft[self.SpawnIndex].despawnme=false -self.ratcraft[self.SpawnIndex].nrespawn=nrespawn -if self.f10menu then -local name=self.aircraft.type.." ID "..tostring(self.SpawnIndex) -self.Menu[self.SubMenuName].groups[self.SpawnIndex]=MENU_MISSION:New(name,self.Menu[self.SubMenuName].groups) -self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"]=MENU_MISSION:New("Set ROE",self.Menu[self.SubMenuName].groups[self.SpawnIndex]) -MENU_MISSION_COMMAND:New("Weapons hold",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"],self._SetROE,self,group,RAT.ROE.weaponhold) -MENU_MISSION_COMMAND:New("Weapons free",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"],self._SetROE,self,group,RAT.ROE.weaponfree) -MENU_MISSION_COMMAND:New("Return fire",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"],self._SetROE,self,group,RAT.ROE.returnfire) -self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"]=MENU_MISSION:New("Set ROT",self.Menu[self.SubMenuName].groups[self.SpawnIndex]) -MENU_MISSION_COMMAND:New("No reaction",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.noreaction) -MENU_MISSION_COMMAND:New("Passive defense",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.passive) -MENU_MISSION_COMMAND:New("Evade on fire",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.evade) -MENU_MISSION_COMMAND:New("Despawn group",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self._Despawn,self,group) -MENU_MISSION_COMMAND:New("Place markers",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self._PlaceMarkers,self,waypoints,self.SpawnIndex) -MENU_MISSION_COMMAND:New("Status report",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self.Status,self,true,self.SpawnIndex) -end -return self.SpawnIndex -end -function RAT:ClearForLanding(name) -trigger.action.setUserFlag(name,1) -local flagvalue=trigger.misc.getUserFlag(name) -self:T(RAT.id.."ATC: User flag value (landing) for "..name.." set to "..flagvalue) -end -function RAT:_Respawn(index,lastpos,delay) -local departure=self.ratcraft[index].departure -local destination=self.ratcraft[index].destination -local takeoff=self.ratcraft[index].takeoff -local landing=self.ratcraft[index].landing -local livery=self.ratcraft[index].livery -local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints] -local _departure=nil -local _destination=nil -local _takeoff=nil -local _landing=nil -local _livery=nil -local _lastwp=nil -local _lastpos=nil -if self.continuejourney then -_departure=destination:GetName() -_livery=livery -if landing==RAT.wp.landing and lastpos and not(self.respawn_at_landing or self.respawn_after_takeoff)then -if destination:GetCategory()==4 then -_lastpos=lastpos -end -end -if self.destinationzone then -_takeoff=RAT.wp.air -_landing=RAT.wp.air -elseif self.returnzone then -_takeoff=self.takeoff -if self.takeoff==RAT.wp.air then -_landing=RAT.wp.air -else -_landing=RAT.wp.landing -end -_departure=departure:GetName() -else -_takeoff=self.takeoff -_landing=self.landing -end -elseif self.commute then -if self.starshape==true then -if destination:GetName()==self.homebase then -_departure=self.homebase -_destination=nil -else -_departure=destination:GetName() -_destination=self.homebase -end -else -_departure=destination:GetName() -_destination=departure:GetName() -end -_livery=livery -if landing==RAT.wp.landing and lastpos and not(self.respawn_at_landing or self.respawn_after_takeoff)then -if destination:GetCategory()==4 then -_lastpos=lastpos -end -end -if self.destinationzone then -if self.takeoff==RAT.wp.air then -_takeoff=RAT.wp.air -_landing=RAT.wp.air -else -if takeoff==RAT.wp.air then -_takeoff=self.takeoff -_landing=RAT.wp.air -else -_takeoff=RAT.wp.air -_landing=RAT.wp.landing -end -end -elseif self.returnzone then -_departure=departure:GetName() -_destination=destination:GetName() -_takeoff=self.takeoff -_landing=self.landing -end -end -if _takeoff==RAT.wp.air and(self.continuejourney or self.commute)then -_lastwp=lastwp -end -self:T2({departure=_departure,destination=_destination,takeoff=_takeoff,landing=_landing,livery=_livery,lastwp=_lastwp}) -local respawndelay -if delay then -respawndelay=delay -elseif self.respawn_delay then -respawndelay=self.respawn_delay+3 -else -respawndelay=3 -end -local arg={} -arg.self=self -arg.departure=_departure -arg.destination=_destination -arg.takeoff=_takeoff -arg.landing=_landing -arg.livery=_livery -arg.lastwp=_lastwp -arg.lastpos=_lastpos -self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.",self.alias,respawndelay)) -SCHEDULER:New(nil,self._SpawnWithRouteTimer,{arg},respawndelay) -end -function RAT._SpawnWithRouteTimer(arg) -RAT._SpawnWithRoute(arg.self,arg.departure,arg.destination,arg.takeoff,arg.landing,arg.livery,arg.lastwp,arg.lastpos) -end -function RAT:_SetRoute(takeoff,landing,_departure,_destination,_waypoint) -local VxCruiseMax -if self.Vcruisemax then -VxCruiseMax=math.min(self.Vcruisemax,self.aircraft.Vmax) -else -VxCruiseMax=math.min(self.aircraft.Vmax*0.90,250) -end -local VxCruiseMin=math.min(VxCruiseMax*0.70,166) -local VxCruise=UTILS.RandomGaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin,(VxCruiseMax-VxCruiseMax)/4,VxCruiseMin,VxCruiseMax) -local VxClimb=math.min(self.aircraft.Vmax*0.90,200) -local VxDescent=math.min(self.aircraft.Vmax*0.60,140) -local VxHolding=VxDescent*0.9 -local VxFinal=VxHolding*0.9 -local VyClimb=math.min(self.Vclimb*RAT.unit.ft2meter/60,self.aircraft.Vymax) -local AlphaClimb=math.asin(VyClimb/VxClimb) -local AlphaDescent=math.rad(self.AlphaDescent) -local FLcruise_expect=self.FLcruise -local departure=nil -if _departure then -if self:_AirportExists(_departure)then -departure=AIRBASE:FindByName(_departure) -if takeoff==RAT.wp.air then -departure=departure:GetZone() -end -elseif self:_ZoneExists(_departure)then -departure=ZONE:New(_departure) -else -local text=string.format("ERROR! Specified departure airport %s does not exist for %s.",_departure,self.alias) -self:E(RAT.id..text) -end -else -departure=self:_PickDeparture(takeoff) -if self.commute and self.starshape==true and self.homebase==nil then -self.homebase=departure:GetName() -end -end -if not departure then -local text=string.format("ERROR! No valid departure airport could be found for %s.",self.alias) -self:E(RAT.id..text) -return nil -end -local Pdeparture -if takeoff==RAT.wp.air then -if _waypoint then -Pdeparture=COORDINATE:New(_waypoint.x,_waypoint.alt,_waypoint.y) -else -local vec2=departure:GetRandomVec2() -Pdeparture=COORDINATE:NewFromVec2(vec2) -end -else -Pdeparture=departure:GetCoordinate() -end -local H_departure -if takeoff==RAT.wp.air then -local Hmin -if self.category==RAT.cat.plane then -Hmin=1000 -else -Hmin=50 -end -H_departure=self:_Randomize(FLcruise_expect*0.7,0.3,Pdeparture.y+Hmin,FLcruise_expect) -if self.FLminuser then -H_departure=math.max(H_departure,self.FLminuser) -end -if _waypoint then -H_departure=_waypoint.alt -end -else -H_departure=Pdeparture.y -end -local mindist=self.mindist -if self.FLminuser then -local hclimb=self.FLminuser-H_departure -local hdescent=self.FLminuser-H_departure -local Dclimb,Ddescent,Dtot=self:_MinDistance(AlphaClimb,AlphaDescent,hclimb,hdescent) -if takeoff==RAT.wp.air and landing==RAT.wpair then -mindist=0 -elseif takeoff==RAT.wp.air then -mindist=Ddescent -elseif landing==RAT.wp.air then -mindist=Dclimb -else -mindist=Dtot -end -mindist=math.max(self.mindist,mindist) -local text=string.format("Adjusting min distance to %d km (for given min FL%03d)",mindist/1000,self.FLminuser/RAT.unit.FL2m) -self:T(RAT.id..text) -end -local destination=nil -if _destination then -if self:_AirportExists(_destination)then -destination=AIRBASE:FindByName(_destination) -if landing==RAT.wp.air or self.returnzone then -destination=destination:GetZone() -end -elseif self:_ZoneExists(_destination)then -destination=ZONE:New(_destination) -else -local text=string.format("ERROR: Specified destination airport/zone %s does not exist for %s!",_destination,self.alias) -self:E(RAT.id.."ERROR: "..text) -end -else -local random=self.random_destination -if self.continuejourney and _departure and#self.destination_ports<3 then -random=true -end -local mylanding=landing -local acrange=self.aircraft.Reff -if self.returnzone then -mylanding=RAT.wp.air -acrange=self.aircraft.Reff/2 -end -destination=self:_PickDestination(departure,Pdeparture,mindist,math.min(acrange,self.maxdist),random,mylanding) -end -if not destination then -local text=string.format("No valid destination airport could be found for %s!",self.alias) -MESSAGE:New(text,60):ToAll() -self:E(RAT.id.."ERROR: "..text) -return nil -end -if destination:GetName()==departure:GetName()then -local text=string.format("%s: Destination and departure are identical. Airport/zone %s.",self.alias,destination:GetName()) -MESSAGE:New(text,30):ToAll() -self:E(RAT.id.."ERROR: "..text) -end -local Preturn -local destination_returnzone -if self.returnzone then -local vec2=destination:GetRandomVec2() -Preturn=COORDINATE:NewFromVec2(vec2) -destination_returnzone=destination -destination=departure -end -local Pdestination -if landing==RAT.wp.air then -local vec2=destination:GetRandomVec2() -Pdestination=COORDINATE:NewFromVec2(vec2) -else -Pdestination=destination:GetCoordinate() -end -local H_destination=Pdestination.y -local Rhmin=8000 -local Rhmax=20000 -if self.category==RAT.cat.heli then -Rhmin=500 -Rhmax=1000 -end -local Vholding=Pdestination:GetRandomVec2InRadius(Rhmax,Rhmin) -local Pholding=COORDINATE:NewFromVec2(Vholding) -local H_holding=Pholding.y -local h_holding -if self.category==RAT.cat.plane then -h_holding=1200 -else -h_holding=150 -end -h_holding=self:_Randomize(h_holding,0.2) -local Hh_holding=H_holding+h_holding -if landing==RAT.wp.air then -Hh_holding=H_departure -end -local d_holding=Pholding:Get2DDistance(Pdestination) -local heading -local d_total -if self.returnzone then -heading=self:_Course(Pdeparture,Preturn) -d_total=Pdeparture:Get2DDistance(Preturn)+Preturn:Get2DDistance(Pholding) -else -heading=self:_Course(Pdeparture,Pholding) -d_total=Pdeparture:Get2DDistance(Pholding) -end -if takeoff==RAT.wp.air then -local H_departure_max -if landing==RAT.wp.air then -H_departure_max=H_departure -else -H_departure_max=d_total*math.tan(AlphaDescent)+Hh_holding -end -H_departure=math.min(H_departure,H_departure_max) -end -local deltaH=math.abs(H_departure-Hh_holding) -local phi=math.atan(deltaH/d_total) -local phi_climb -local phi_descent -if(H_departure>Hh_holding)then -phi_climb=AlphaClimb+phi -phi_descent=AlphaDescent-phi -else -phi_climb=AlphaClimb-phi -phi_descent=AlphaDescent+phi -end -local D_total -if self.returnzone then -D_total=math.sqrt(deltaH*deltaH+d_total/2*d_total/2) -else -D_total=math.sqrt(deltaH*deltaH+d_total*d_total) -end -local gamma=math.rad(180)-phi_climb-phi_descent -local a=D_total*math.sin(phi_climb)/math.sin(gamma) -local b=D_total*math.sin(phi_descent)/math.sin(gamma) -local hphi_max=b*math.sin(phi_climb) -local hphi_max2=a*math.sin(phi_descent) -local h_max1=b*math.sin(AlphaClimb) -local h_max2=a*math.sin(AlphaDescent) -local h_max -if(H_departure>Hh_holding)then -h_max=math.min(h_max1,h_max2) -else -h_max=math.max(h_max1,h_max2) -end -local FLmax=h_max+H_departure -local FLmin=math.max(H_departure,Hh_holding) -if self.category==RAT.cat.heli then -FLmin=math.max(H_departure,H_destination)+50 -FLmax=math.max(H_departure,H_destination)+1000 -end -FLmax=math.min(FLmax,self.aircraft.ceiling) -if self.FLminuser then -FLmin=math.max(self.FLminuser,FLmin) -end -if self.FLmaxuser then -FLmax=math.min(self.FLmaxuser,FLmax) -end -if FLmin>FLmax then -FLmin=FLmax -end -if FLcruise_expectFLmax then -FLcruise_expect=FLmax -end -local FLcruise=UTILS.RandomGaussian(FLcruise_expect,math.abs(FLmax-FLmin)/4,FLmin,FLmax) -if self.FLuser then -FLcruise=self.FLuser -FLcruise=math.max(FLcruise,FLmin) -FLcruise=math.min(FLcruise,FLmax) -end -local h_climb=FLcruise-H_departure -local h_descent=FLcruise-Hh_holding -local d_climb=h_climb/math.tan(AlphaClimb) -local d_descent=h_descent/math.tan(AlphaDescent) -local d_cruise=d_total-d_climb-d_descent -local text=string.format("\n******************************************************\n") -text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix) -text=text..string.format("Alias = %s\n",self.alias) -text=text..string.format("Group name = %s\n\n",self:_AnticipatedGroupName()) -text=text..string.format("Speeds:\n") -text=text..string.format("VxCruiseMin = %6.1f m/s = %5.1f km/h\n",VxCruiseMin,VxCruiseMin*3.6) -text=text..string.format("VxCruiseMax = %6.1f m/s = %5.1f km/h\n",VxCruiseMax,VxCruiseMax*3.6) -text=text..string.format("VxCruise = %6.1f m/s = %5.1f km/h\n",VxCruise,VxCruise*3.6) -text=text..string.format("VxClimb = %6.1f m/s = %5.1f km/h\n",VxClimb,VxClimb*3.6) -text=text..string.format("VxDescent = %6.1f m/s = %5.1f km/h\n",VxDescent,VxDescent*3.6) -text=text..string.format("VxHolding = %6.1f m/s = %5.1f km/h\n",VxHolding,VxHolding*3.6) -text=text..string.format("VxFinal = %6.1f m/s = %5.1f km/h\n",VxFinal,VxFinal*3.6) -text=text..string.format("VyClimb = %6.1f m/s\n",VyClimb) -text=text..string.format("\nDistances:\n") -text=text..string.format("d_climb = %6.1f km\n",d_climb/1000) -text=text..string.format("d_cruise = %6.1f km\n",d_cruise/1000) -text=text..string.format("d_descent = %6.1f km\n",d_descent/1000) -text=text..string.format("d_holding = %6.1f km\n",d_holding/1000) -text=text..string.format("d_total = %6.1f km\n",d_total/1000) -text=text..string.format("\nHeights:\n") -text=text..string.format("H_departure = %6.1f m ASL\n",H_departure) -text=text..string.format("H_destination = %6.1f m ASL\n",H_destination) -text=text..string.format("H_holding = %6.1f m ASL\n",H_holding) -text=text..string.format("h_climb = %6.1f m\n",h_climb) -text=text..string.format("h_descent = %6.1f m\n",h_descent) -text=text..string.format("h_holding = %6.1f m\n",h_holding) -text=text..string.format("delta H = %6.1f m\n",deltaH) -text=text..string.format("FLmin = %6.1f m ASL = FL%03d\n",FLmin,FLmin/RAT.unit.FL2m) -text=text..string.format("FLcruise = %6.1f m ASL = FL%03d\n",FLcruise,FLcruise/RAT.unit.FL2m) -text=text..string.format("FLmax = %6.1f m ASL = FL%03d\n",FLmax,FLmax/RAT.unit.FL2m) -text=text..string.format("\nAngles:\n") -text=text..string.format("Alpha climb = %6.2f Deg\n",math.deg(AlphaClimb)) -text=text..string.format("Alpha descent = %6.2f Deg\n",math.deg(AlphaDescent)) -text=text..string.format("Phi (slope) = %6.2f Deg\n",math.deg(phi)) -text=text..string.format("Phi climb = %6.2f Deg\n",math.deg(phi_climb)) -text=text..string.format("Phi descent = %6.2f Deg\n",math.deg(phi_descent)) -if self.Debug then -local h_climb_max=FLmax-H_departure -local h_descent_max=FLmax-Hh_holding -local d_climb_max=h_climb_max/math.tan(AlphaClimb) -local d_descent_max=h_descent_max/math.tan(AlphaDescent) -local d_cruise_max=d_total-d_climb_max-d_descent_max -text=text..string.format("Heading = %6.1f Deg\n",heading) -text=text..string.format("\nSSA triangle:\n") -text=text..string.format("D_total = %6.1f km\n",D_total/1000) -text=text..string.format("gamma = %6.1f Deg\n",math.deg(gamma)) -text=text..string.format("a = %6.1f m\n",a) -text=text..string.format("b = %6.1f m\n",b) -text=text..string.format("hphi_max = %6.1f m\n",hphi_max) -text=text..string.format("hphi_max2 = %6.1f m\n",hphi_max2) -text=text..string.format("h_max1 = %6.1f m\n",h_max1) -text=text..string.format("h_max2 = %6.1f m\n",h_max2) -text=text..string.format("h_max = %6.1f m\n",h_max) -text=text..string.format("\nMax heights and distances:\n") -text=text..string.format("d_climb_max = %6.1f km\n",d_climb_max/1000) -text=text..string.format("d_cruise_max = %6.1f km\n",d_cruise_max/1000) -text=text..string.format("d_descent_max = %6.1f km\n",d_descent_max/1000) -text=text..string.format("h_climb_max = %6.1f m\n",h_climb_max) -text=text..string.format("h_descent_max = %6.1f m\n",h_descent_max) -end -text=text..string.format("******************************************************\n") -self:T2(RAT.id..text) -if d_cruise<0 then -d_cruise=100 -end -local wp={} -local c={} -local wpholding=nil -local wpfinal=nil -c[#c+1]=Pdeparture -wp[#wp+1]=self:_Waypoint(#wp+1,"Departure",takeoff,c[#wp+1],VxClimb,H_departure,departure) -self.waypointdescriptions[#wp]="Departure" -self.waypointstatus[#wp]=RAT.status.Departure -if takeoff==RAT.wp.air then -if d_climb<5000 or d_cruise<5000 then -d_cruise=d_cruise+d_climb -else -c[#c+1]=c[#c]:Translate(d_climb,heading) -wp[#wp+1]=self:_Waypoint(#wp+1,"Begin of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) -self.waypointdescriptions[#wp]="Begin of Cruise" -self.waypointstatus[#wp]=RAT.status.Cruise -end -else -c[#c+1]=c[#c]:Translate(d_climb/2,heading) -c[#c+1]=c[#c]:Translate(d_climb/2,heading) -wp[#wp+1]=self:_Waypoint(#wp+1,"Climb",RAT.wp.climb,c[#wp+1],VxClimb,H_departure+(FLcruise-H_departure)/2) -self.waypointdescriptions[#wp]="Climb" -self.waypointstatus[#wp]=RAT.status.Climb -wp[#wp+1]=self:_Waypoint(#wp+1,"Begin of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) -self.waypointdescriptions[#wp]="Begin of Cruise" -self.waypointstatus[#wp]=RAT.status.Cruise -end -if self.returnzone then -c[#c+1]=Preturn -wp[#wp+1]=self:_Waypoint(#wp+1,"Return Zone",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) -self.waypointdescriptions[#wp]="Return Zone" -self.waypointstatus[#wp]=RAT.status.Uturn -end -if landing==RAT.wp.air then -c[#c+1]=Pdestination -wp[#wp+1]=self:_Waypoint(#wp+1,"Final Destination",RAT.wp.finalwp,c[#wp+1],VxCruise,FLcruise) -self.waypointdescriptions[#wp]="Final Destination" -self.waypointstatus[#wp]=RAT.status.Destination -elseif self.returnzone then -c[#c+1]=c[#c]:Translate(d_cruise/2,heading-180) -wp[#wp+1]=self:_Waypoint(#wp+1,"End of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) -self.waypointdescriptions[#wp]="End of Cruise" -self.waypointstatus[#wp]=RAT.status.Descent -else -c[#c+1]=c[#c]:Translate(d_cruise,heading) -wp[#wp+1]=self:_Waypoint(#wp+1,"End of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) -self.waypointdescriptions[#wp]="End of Cruise" -self.waypointstatus[#wp]=RAT.status.Descent -end -if landing==RAT.wp.landing then -if self.returnzone then -c[#c+1]=c[#c]:Translate(d_descent/2,heading-180) -wp[#wp+1]=self:_Waypoint(#wp+1,"Descent",RAT.wp.descent,c[#wp+1],VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2) -self.waypointdescriptions[#wp]="Descent" -self.waypointstatus[#wp]=RAT.status.DescentHolding -else -c[#c+1]=c[#c]:Translate(d_descent/2,heading) -wp[#wp+1]=self:_Waypoint(#wp+1,"Descent",RAT.wp.descent,c[#wp+1],VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2) -self.waypointdescriptions[#wp]="Descent" -self.waypointstatus[#wp]=RAT.status.DescentHolding -end -end -if landing==RAT.wp.landing then -c[#c+1]=Pholding -wp[#wp+1]=self:_Waypoint(#wp+1,"Holding Point",RAT.wp.holding,c[#wp+1],VxHolding,H_holding+h_holding) -self.waypointdescriptions[#wp]="Holding Point" -self.waypointstatus[#wp]=RAT.status.Holding -wpholding=#wp -c[#c+1]=Pdestination -wp[#wp+1]=self:_Waypoint(#wp+1,"Final Destination",landing,c[#wp+1],VxFinal,H_destination,destination) -self.waypointdescriptions[#wp]="Final Destination" -self.waypointstatus[#wp]=RAT.status.Destination -end -wpfinal=#wp -local waypoints={} -for _,p in ipairs(wp)do -table.insert(waypoints,p) -end -self:_Routeinfo(waypoints,"Waypoint info in set_route:") -if self.returnzone then -return departure,destination_returnzone,waypoints,wpholding,wpfinal -else -return departure,destination,waypoints,wpholding,wpfinal -end -end -function RAT:_PickDeparture(takeoff) -local departures={} -if self.random_departure then -for _,_airport in pairs(self.airports)do -local airport=_airport -local name=airport:GetName() -if not self:_Excluded(name)then -if takeoff==RAT.wp.air then -table.insert(departures,airport:GetZone()) -else -local nspots=1 -if self.termtype~=nil then -nspots=airport:GetParkingSpotsNumber(self.termtype) -end -if nspots>0 then -table.insert(departures,airport) -end -end -end -end -else -for _,name in pairs(self.departure_ports)do -local dep=nil -if self:_AirportExists(name)then -if takeoff==RAT.wp.air then -dep=AIRBASE:FindByName(name):GetZone() -else -dep=AIRBASE:FindByName(name) -if self.termtype~=nil and dep~=nil then -local _dep=dep -local nspots=_dep:GetParkingSpotsNumber(self.termtype) -if nspots==0 then -dep=nil -end -end -end -elseif self:_ZoneExists(name)then -if takeoff==RAT.wp.air then -dep=ZONE:New(name) -else -self:E(RAT.id..string.format("ERROR! Takeoff is not in air. Cannot use %s as departure.",name)) -end -else -self:E(RAT.id..string.format("ERROR: No airport or zone found with name %s.",name)) -end -if dep then -table.insert(departures,dep) -end -end -end -self:T(RAT.id..string.format("Number of possible departures for %s= %d",self.alias,#departures)) -local departure=departures[math.random(#departures)] -local text -if departure and departure:GetName()then -if takeoff==RAT.wp.air then -text=string.format("%s: Chosen departure zone: %s",self.alias,departure:GetName()) -else -text=string.format("%s: Chosen departure airport: %s (ID %d)",self.alias,departure:GetName(),departure:GetID()) -end -self:T(RAT.id..text) -else -self:E(RAT.id..string.format("ERROR! No departure airport or zone found for %s.",self.alias)) -departure=nil -end -return departure -end -function RAT:_PickDestination(departure,q,minrange,maxrange,random,landing) -minrange=minrange or self.mindist -maxrange=maxrange or self.maxdist -local destinations={} -if random then -for _,_airport in pairs(self.airports)do -local airport=_airport -local name=airport:GetName() -if self:_IsFriendly(name)and not self:_Excluded(name)and name~=departure:GetName()then -local distance=q:Get2DDistance(airport:GetCoordinate()) -if distance>=minrange and distance<=maxrange then -if landing==RAT.wp.air then -table.insert(destinations,airport:GetZone()) -else -local nspot=1 -if self.termtype then -nspot=airport:GetParkingSpotsNumber(self.termtype) -end -if nspot>0 then -table.insert(destinations,airport) -end -end -end -end -end -else -for _,name in pairs(self.destination_ports)do -if name~=departure:GetName()then -local dest=nil -if self:_AirportExists(name)then -if landing==RAT.wp.air then -dest=AIRBASE:FindByName(name):GetZone() -else -dest=AIRBASE:FindByName(name) -local nspot=1 -if self.termtype then -nspot=dest:GetParkingSpotsNumber(self.termtype) -end -if nspot==0 then -dest=nil -end -end -elseif self:_ZoneExists(name)then -if landing==RAT.wp.air then -dest=ZONE:New(name) -else -self:E(RAT.id..string.format("ERROR! Landing is not in air. Cannot use zone %s as destination!",name)) -end -else -self:E(RAT.id..string.format("ERROR! No airport or zone found with name %s",name)) -end -if dest then -local distance=q:Get2DDistance(dest:GetCoordinate()) -if distance>=minrange and distance<=maxrange then -table.insert(destinations,dest) -else -local text=string.format("Destination %s is ouside range. Distance = %5.1f km, min = %5.1f km, max = %5.1f km.",name,distance,minrange,maxrange) -self:T(RAT.id..text) -end -end -end -end -end -self:T(RAT.id..string.format("Number of possible destinations = %s.",#destinations)) -if#destinations>0 then -local function compare(a,b) -local qa=q:Get2DDistance(a:GetCoordinate()) -local qb=q:Get2DDistance(b:GetCoordinate()) -return qa0 then -destination=destinations[math.random(#destinations)] -local text -if landing==RAT.wp.air then -text=string.format("%s: Chosen destination zone: %s.",self.alias,destination:GetName()) -else -text=string.format("%s Chosen destination airport: %s (ID %d).",self.alias,destination:GetName(),destination:GetID()) -end -self:T(RAT.id..text) -else -self:E(RAT.id.."ERROR! No destination airport or zone found.") -destination=nil -end -return destination -end -function RAT:_GetAirportsInZone(zone) -local airports={} -for _,airport in pairs(self.airports)do -local name=airport:GetName() -local coord=airport:GetCoordinate() -if zone:IsPointVec3InZone(coord)then -table.insert(airports,name) -end -end -return airports -end -function RAT:_Excluded(port) -for _,name in pairs(self.excluded_ports)do -if name==port then -return true -end -end -return false -end -function RAT:_IsFriendly(port) -for _,airport in pairs(self.airports)do -local name=airport:GetName() -if name==port then -return true -end -end -return false -end -function RAT:_GetAirportsOfMap() -local _coalition -for i=0,2 do -if i==0 then -_coalition=coalition.side.NEUTRAL -elseif i==1 then -_coalition=coalition.side.RED -elseif i==2 then -_coalition=coalition.side.BLUE -end -local ab=coalition.getAirbases(i) -for _,airbase in pairs(ab)do -local _id=airbase:getID() -local _p=airbase:getPosition().p -local _name=airbase:getName() -local _myab=AIRBASE:FindByName(_name) -if _myab then -table.insert(self.airports_map,_myab) -local text="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName() -self:T(RAT.id..text) -else -self:E(RAT.id..string.format("WARNING: Airbase %s does not exsist as MOOSE object!",tostring(_name))) -end -end -end -end -function RAT:_GetAirportsOfCoalition() -for _,coalition in pairs(self.ctable)do -for _,_airport in pairs(self.airports_map)do -local airport=_airport -local category=airport:GetAirbaseCategory() -if airport:GetCoalition()==coalition then -local condition1=self.category==RAT.cat.plane and category==Airbase.Category.HELIPAD -local condition2=self.category==RAT.cat.plane and category==Airbase.Category.SHIP -if not(condition1 or condition2)then -table.insert(self.airports,airport) -end -end -end -end -if#self.airports==0 then -local text=string.format("No possible departure/destination airports found for RAT %s.",tostring(self.alias)) -MESSAGE:New(text,10):ToAll() -self:E(RAT.id..text) -end -end -function RAT:Status(message,forID) -if message==nil then -message=false -end -if forID==nil then -forID=false -end -local Tnow=timer.getTime() -local nalive=0 -for spawnindex,ratcraft in ipairs(self.ratcraft)do -local group=ratcraft.group -if group and group:IsAlive()then -nalive=nalive+1 -local prefix=self:_GetPrefixFromGroup(group) -local life=self:_GetLife(group) -local fuel=group:GetFuel()*100.0 -local airborne=group:InAir() -local coords=group:GetCoordinate() -local alt=coords.y -local departure=ratcraft.departure:GetName() -local destination=ratcraft.destination:GetName() -local type=self.aircraft.type -local status=ratcraft.status -local active=ratcraft.active -local Nunits=ratcraft.nunits -local N0units=group:GetInitialSize() -local Tg=0 -local Dg=0 -local dTlast=0 -local stationary=false -if airborne then -ratcraft["Tground"]=nil -ratcraft["Pground"]=nil -ratcraft["Uground"]=nil -ratcraft["Tlastcheck"]=nil -else -if ratcraft["Tground"]then -Tg=Tnow-ratcraft["Tground"] -Dg=coords:Get2DDistance(ratcraft["Pground"]) -dTlast=Tnow-ratcraft["Tlastcheck"] -if dTlast>self.Tinactive then -for _,_unit in pairs(group:GetUnits())do -if _unit and _unit:IsAlive()then -local unitname=_unit:GetName() -local unitcoord=_unit:GetCoordinate() -local Ug=unitcoord:Get2DDistance(ratcraft.Uground[unitname]) -self:T2(RAT.id..string.format("Unit %s travelled distance on ground %.1f m since %d seconds.",unitname,Ug,dTlast)) -if Ug<50 and active and status~=RAT.status.EventBirth then -stationary=true -end -ratcraft["Uground"][unitname]=unitcoord -end -end -ratcraft["Tlastcheck"]=Tnow -ratcraft["Pground"]=coords -end -else -ratcraft["Tground"]=Tnow -ratcraft["Tlastcheck"]=Tnow -ratcraft["Pground"]=coords -ratcraft["Uground"]={} -for _,_unit in pairs(group:GetUnits())do -local unitname=_unit:GetName() -ratcraft.Uground[unitname]=_unit:GetCoordinate() -end -end -end -local Pn=coords -local Dtravel=Pn:Get2DDistance(ratcraft["Pnow"]) -ratcraft["Pnow"]=Pn -ratcraft["Distance"]=ratcraft["Distance"]+Dtravel -local Ddestination=Pn:Get2DDistance(ratcraft.destination:GetCoordinate()) -if(forID and spawnindex==forID)or(not forID)then -local text=string.format("ID %i of flight %s",spawnindex,prefix) -if N0units>1 then -text=text..string.format(" (%d/%d)\n",Nunits,N0units) -else -text=text.."\n" -end -if self.commute then -text=text..string.format("%s commuting between %s and %s\n",type,departure,destination) -elseif self.continuejourney then -text=text..string.format("%s travelling from %s to %s (and continueing form there)\n",type,departure,destination) -else -text=text..string.format("%s travelling from %s to %s\n",type,departure,destination) -end -text=text..string.format("Status: %s",status) -if airborne then -text=text.." [airborne]\n" -else -text=text.." [on ground]\n" -end -text=text..string.format("Fuel = %3.0f %%\n",fuel) -text=text..string.format("Life = %3.0f %%\n",life) -text=text..string.format("FL%03d = %i m ASL\n",alt/RAT.unit.FL2m,alt) -text=text..string.format("Distance travelled = %6.1f km\n",ratcraft["Distance"]/1000) -text=text..string.format("Distance to destination = %6.1f km",Ddestination/1000) -if not airborne then -text=text..string.format("\nTime on ground = %6.0f seconds\n",Tg) -text=text..string.format("Position change = %8.1f m since %3.0f seconds.",Dg,dTlast) -end -self:T(RAT.id..text) -if message then -MESSAGE:New(text,20):ToAll() -end -end -if not airborne then -if stationary then -local text=string.format("Group %s is despawned after being %d seconds inaktive on ground.",self.alias,dTlast) -self:T(RAT.id..text) -self:_Despawn(group) -end -if life<10 and Dtravel<100 then -local text=string.format("Damaged group %s is despawned. Life = %3.0f",self.alias,life) -self:T(RAT.id..text) -self:_Despawn(group) -end -end -if ratcraft.despawnme then -local text=string.format("Flight %s will be despawned NOW!",self.alias) -self:T(RAT.id..text) -if(not self.norespawn)and(not self.respawn_after_takeoff)then -local idx=self:GetSpawnIndexFromGroup(group) -local coord=group:GetCoordinate() -self:_Respawn(idx,coord,0) -end -if self.despawnair then -self:_Despawn(group,0) -end -end -else -local text=string.format("Group does not exist in loop ratcraft status.") -self:T2(RAT.id..text) -end -end -local text=string.format("Alive groups of %s: %d, nalive=%d/%d",self.alias,self.alive,nalive,self.ngroups) -self:T(RAT.id..text) -MESSAGE:New(text,20):ToAllIf(message and not forID) -end -function RAT:_GetLife(group) -local life=0.0 -if group and group:IsAlive()then -local unit=group:GetUnit(1) -if unit then -life=unit:GetLife()/unit:GetLife0()*100 -else -self:T2(RAT.id.."ERROR! Unit does not exist in RAT_Getlife(). Returning zero.") -end -else -self:T2(RAT.id.."ERROR! Group does not exist in RAT_Getlife(). Returning zero.") -end -return life -end -function RAT:_SetStatus(group,status) -if group and group:IsAlive()then -local index=self:GetSpawnIndexFromGroup(group) -if self.ratcraft[index]then -self.ratcraft[index].status=status -local no1=status==RAT.status.Departure -local no2=status==RAT.status.EventBirthAir -local no3=status==RAT.status.Holding -local text=string.format("Flight %s: %s.",group:GetName(),status) -self:T(RAT.id..text) -if not(no1 or no2 or no3)then -MESSAGE:New(text,10):ToAllIf(self.reportstatus) -end -end -end -end -function RAT:GetStatus(group) -if group and group:IsAlive()then -local index=self:GetSpawnIndexFromGroup(group) -if self.ratcraft[index]then -return self.ratcraft[index].status -end -end -return"nonexistant" -end -function RAT:_OnBirth(EventData) -self:F3(EventData) -self:T3(RAT.id.."Captured event birth!") -local SpawnGroup=EventData.IniGroup -if SpawnGroup then -local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup) -if EventPrefix then -if EventPrefix==self.alias then -local text="Event: Group "..SpawnGroup:GetName().." was born." -self:T(RAT.id..text) -local status="unknown in birth" -if SpawnGroup:InAir()then -status=RAT.status.EventBirthAir -elseif self.uncontrolled then -status=RAT.status.Uncontrolled -else -status=RAT.status.EventBirth -end -self:_SetStatus(SpawnGroup,status) -local i=self:GetSpawnIndexFromGroup(SpawnGroup) -local _departure=self.ratcraft[i].departure:GetName() -local _destination=self.ratcraft[i].destination:GetName() -local _nrespawn=self.ratcraft[i].nrespawn -local _takeoff=self.ratcraft[i].takeoff -local _landing=self.ratcraft[i].landing -local _livery=self.ratcraft[i].livery -local _airbase=AIRBASE:FindByName(_departure) -local onrunway=false -if _airbase then -if self.checkonrunway and _takeoff~=RAT.wp.runway and _takeoff~=RAT.wp.air then -onrunway=_airbase:CheckOnRunWay(SpawnGroup,self.onrunwayradius,false) -end -end -if onrunway then -local text=string.format("ERROR: RAT group of %s was spawned on runway. Group #%d will be despawned immediately!",self.alias,i) -MESSAGE:New(text,30):ToAllIf(self.Debug) -self:E(RAT.id..text) -if self.Debug then -SpawnGroup:FlareRed() -end -self:_Despawn(SpawnGroup) -if(self.Ndeparture_Airports>=2 or self.random_departure)and _nrespawn new state %s.",SpawnGroup:GetName(),currentstate,status) -self:T(RAT.id..text) -local idx=self:GetSpawnIndexFromGroup(SpawnGroup) -local coord=SpawnGroup:GetCoordinate() -self:_Respawn(idx,coord) -end -text="Event: Group "..SpawnGroup:GetName().." will be destroyed now." -self:T(RAT.id..text) -self:_Despawn(SpawnGroup) -end -end -end -else -self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnEngineShutdown().") -end -end -function RAT:_OnHit(EventData) -self:F3(EventData) -self:T(RAT.id..string.format("Captured event Hit by %s! Initiator %s. Target %s",self.alias,tostring(EventData.IniUnitName),tostring(EventData.TgtUnitName))) -local SpawnGroup=EventData.TgtGroup -if SpawnGroup then -local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup) -if EventPrefix and EventPrefix==self.alias then -self:T(RAT.id..string.format("Event: Group %s was hit. Unit %s.",SpawnGroup:GetName(),tostring(EventData.TgtUnitName))) -local text=string.format("%s, unit %s was hit!",self.alias,EventData.TgtUnitName) -MESSAGE:New(text,10):ToAllIf(self.reportstatus or self.Debug) -end -end -end -function RAT:_OnDeadOrCrash(EventData) -self:F3(EventData) -self:T3(RAT.id.."Captured event DeadOrCrash!") -local SpawnGroup=EventData.IniGroup -if SpawnGroup then -local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup) -if EventPrefix then -if EventPrefix==self.alias then -self.alive=self.alive-1 -local text=string.format("Event: Group %s crashed or died. Alive counter = %d.",SpawnGroup:GetName(),self.alive) -self:T(RAT.id..text) -if EventData.id==world.event.S_EVENT_CRASH then -self:_OnCrash(EventData) -elseif EventData.id==world.event.S_EVENT_DEAD then -self:_OnDead(EventData) -end -end -end -end -end -function RAT:_OnDead(EventData) -self:F3(EventData) -self:T3(RAT.id.."Captured event Dead!") -local SpawnGroup=EventData.IniGroup -if SpawnGroup then -local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup) -if EventPrefix then -if EventPrefix==self.alias then -local text=string.format("Event: Group %s died. Unit %s.",SpawnGroup:GetName(),EventData.IniUnitName) -self:T(RAT.id..text) -local status=RAT.status.EventDead -self:_SetStatus(SpawnGroup,status) -end -end -else -self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnDead().") -end -end -function RAT:_OnCrash(EventData) -self:F3(EventData) -self:T3(RAT.id.."Captured event Crash!") -local SpawnGroup=EventData.IniGroup -if SpawnGroup then -local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup) -if EventPrefix and EventPrefix==self.alias then -local _i=self:GetSpawnIndexFromGroup(SpawnGroup) -self.ratcraft[_i].nunits=self.ratcraft[_i].nunits-1 -local _n=self.ratcraft[_i].nunits -local _n0=SpawnGroup:GetInitialSize() -local text=string.format("Event: Group %s crashed. Unit %s. Units still alive %d of %d.",SpawnGroup:GetName(),EventData.IniUnitName,_n,_n0) -self:T(RAT.id..text) -local status=RAT.status.EventCrash -self:_SetStatus(SpawnGroup,status) -if _n==0 and self.respawn_after_crash and not self.norespawn then -local text=string.format("No units left of group %s. Group will be respawned now.",SpawnGroup:GetName()) -self:T(RAT.id..text) -local idx=self:GetSpawnIndexFromGroup(SpawnGroup) -local coord=SpawnGroup:GetCoordinate() -self:_Respawn(idx,coord) -end -end -else -if self.Debug then -self:E(RAT.id.."ERROR: Group does not exist in RAT:_OnCrash().") -end -end -end -function RAT:_Despawn(group,delay) -if group~=nil then -local index=self:GetSpawnIndexFromGroup(group) -if index~=nil then -self.ratcraft[index].group=nil -self.ratcraft[index]["status"]="Dead" -local despawndelay=0 -if delay then -despawndelay=delay -elseif self.respawn_delay then -despawndelay=self.respawn_delay -end -self:T(RAT.id..string.format("%s delayed despawn in %.1f seconds.",self.alias,despawndelay)) -SCHEDULER:New(nil,self._Destroy,{self,group},despawndelay) -if self.f10menu and self.SubMenuName~=nil then -self.Menu[self.SubMenuName]["groups"][index]:Remove() -end -end -end -end -function RAT:_Destroy(group) -self:F2(group) -local DCSGroup=group:GetDCSObject() -if DCSGroup and DCSGroup:isExist()then -local triggerdead=true -for _,DCSUnit in pairs(DCSGroup:getUnits())do -if DCSUnit then -if triggerdead then -self:_CreateEventDead(timer.getTime(),DCSUnit) -triggerdead=false -end -_DATABASE:DeleteUnit(DCSUnit:getName()) -end -end -DCSGroup:destroy() -DCSGroup=nil -end -return nil -end -function RAT:_CreateEventDead(EventTime,Initiator) -self:F({EventTime,Initiator}) -local Event={ -id=world.event.S_EVENT_DEAD, -time=EventTime, -initiator=Initiator, -} -world.onEvent(Event) -end -function RAT:_Waypoint(index,description,Type,Coord,Speed,Altitude,Airport) -local _Altitude=Altitude or Coord.y -local Hland=Coord:GetLandHeight() -local _Type=nil -local _Action=nil -local _alttype="RADIO" -if Type==RAT.wp.cold then -_Type="TakeOffParking" -_Action="From Parking Area" -_Altitude=10 -_alttype="RADIO" -elseif Type==RAT.wp.hot then -_Type="TakeOffParkingHot" -_Action="From Parking Area Hot" -_Altitude=10 -_alttype="RADIO" -elseif Type==RAT.wp.runway then -_Type="TakeOff" -_Action="From Parking Area" -_Altitude=10 -_alttype="RADIO" -elseif Type==RAT.wp.air then -_Type="Turning Point" -_Action="Turning Point" -_alttype="BARO" -elseif Type==RAT.wp.climb then -_Type="Turning Point" -_Action="Turning Point" -_alttype="BARO" -elseif Type==RAT.wp.cruise then -_Type="Turning Point" -_Action="Turning Point" -_alttype="BARO" -elseif Type==RAT.wp.descent then -_Type="Turning Point" -_Action="Turning Point" -_alttype="BARO" -elseif Type==RAT.wp.holding then -_Type="Turning Point" -_Action="Turning Point" -_alttype="BARO" -elseif Type==RAT.wp.landing then -_Type="Land" -_Action="Landing" -_Altitude=10 -_alttype="RADIO" -elseif Type==RAT.wp.finalwp then -_Type="Turning Point" -_Action="Turning Point" -_alttype="BARO" -else -self:E(RAT.id.."ERROR: Unknown waypoint type in RAT:Waypoint() function!") -_Type="Turning Point" -_Action="Turning Point" -_alttype="RADIO" -end -local text=string.format("\n******************************************************\n") -text=text..string.format("Waypoint = %d\n",index) -text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix) -text=text..string.format("Alias = %s\n",self.alias) -text=text..string.format("Type: %i - %s\n",Type,_Type) -text=text..string.format("Action: %s\n",_Action) -text=text..string.format("Coord: x = %6.1f km, y = %6.1f km, alt = %6.1f m\n",Coord.x/1000,Coord.z/1000,Coord.y) -text=text..string.format("Speed = %6.1f m/s = %6.1f km/h = %6.1f knots\n",Speed,Speed*3.6,Speed*1.94384) -text=text..string.format("Land = %6.1f m ASL\n",Hland) -text=text..string.format("Altitude = %6.1f m (%s)\n",_Altitude,_alttype) -if Airport then -if Type==RAT.wp.air then -text=text..string.format("Zone = %s\n",Airport:GetName()) -else -text=text..string.format("Airport = %s\n",Airport:GetName()) -end -else -text=text..string.format("No airport/zone specified\n") -end -text=text.."******************************************************\n" -self:T2(RAT.id..text) -local RoutePoint={} -RoutePoint.x=Coord.x -RoutePoint.y=Coord.z -RoutePoint.alt=_Altitude -RoutePoint.alt_type=_alttype -RoutePoint.type=_Type -RoutePoint.action=_Action -RoutePoint.speed=Speed -RoutePoint.speed_locked=true -RoutePoint.ETA=nil -RoutePoint.ETA_locked=false -RoutePoint.name=description -if(Airport~=nil)and(Type~=RAT.wp.air)then -local AirbaseID=Airport:GetID() -local AirbaseCategory=Airport:GetAirbaseCategory() -if AirbaseCategory==Airbase.Category.SHIP then -RoutePoint.linkUnit=AirbaseID -RoutePoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.HELIPAD then -RoutePoint.linkUnit=AirbaseID -RoutePoint.helipadId=AirbaseID -elseif AirbaseCategory==Airbase.Category.AIRDROME then -RoutePoint.airdromeId=AirbaseID -else -self:T(RAT.id.."Unknown Airport category in _Waypoint()!") -end -end -RoutePoint.properties={ -["vnav"]=1, -["scale"]=0, -["angle"]=0, -["vangle"]=0, -["steer"]=2, -} -local TaskCombo={} -local TaskHolding=self:_TaskHolding({x=Coord.x,y=Coord.z},Altitude,Speed,self:_Randomize(90,0.9)) -local TaskWaypoint=self:_TaskFunction("RAT._WaypointFunction",self,index) -RoutePoint.task={} -RoutePoint.task.id="ComboTask" -RoutePoint.task.params={} -TaskCombo[#TaskCombo+1]=TaskWaypoint -if Type==RAT.wp.holding then -TaskCombo[#TaskCombo+1]=TaskHolding -end -RoutePoint.task.params.tasks=TaskCombo -return RoutePoint -end -function RAT:_Routeinfo(waypoints,comment) -local text=string.format("\n******************************************************\n") -text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix) -if comment then -text=text..comment.."\n" -end -text=text..string.format("Number of waypoints = %i\n",#waypoints) -for i=1,#waypoints do -local p=waypoints[i] -text=text..string.format("WP #%i: x = %6.1f km, y = %6.1f km, alt = %6.1f m %s\n",i-1,p.x/1000,p.y/1000,p.alt,self.waypointdescriptions[i]) -end -local total=0.0 -for i=1,#waypoints-1 do -local point1=waypoints[i] -local point2=waypoints[i+1] -local x1=point1.x -local y1=point1.y -local x2=point2.x -local y2=point2.y -local d=math.sqrt((x1-x2)^2+(y1-y2)^2) -local heading=self:_Course(point1,point2) -total=total+d -text=text..string.format("Distance from WP %i-->%i = %6.1f km. Heading = %03d : %s - %s\n",i-1,i,d/1000,heading,self.waypointdescriptions[i],self.waypointdescriptions[i+1]) -end -text=text..string.format("Total distance = %6.1f km\n",total/1000) -text=text..string.format("******************************************************\n") -self:T2(RAT.id..text) -return total -end -function RAT:_TaskHolding(P1,Altitude,Speed,Duration) -local dx=3000 -local dy=0 -if self.category==RAT.cat.heli then -dx=200 -dy=0 -end -local P2={} -P2.x=P1.x+dx -P2.y=P1.y+dy -local Task={ -id='Orbit', -params={ -pattern=AI.Task.OrbitPattern.RACE_TRACK, -point=P1, -point2=P2, -speed=Speed, -altitude=Altitude -} -} -local DCSTask={} -DCSTask.id="ControlledTask" -DCSTask.params={} -DCSTask.params.task=Task -if self.ATCswitch then -local userflagname=string.format("%s#%03d",self.alias,self.SpawnIndex+1) -local maxholdingduration=60*120 -DCSTask.params.stopCondition={userFlag=userflagname,userFlagValue=1,duration=maxholdingduration} -else -DCSTask.params.stopCondition={duration=Duration} -end -return DCSTask -end -function RAT._WaypointFunction(group,rat,wp) -local Tnow=timer.getTime() -local sdx=rat:GetSpawnIndexFromGroup(group) -local departure=rat.ratcraft[sdx].departure:GetName() -local destination=rat.ratcraft[sdx].destination:GetName() -local landing=rat.ratcraft[sdx].landing -local WPholding=rat.ratcraft[sdx].wpholding -local WPfinal=rat.ratcraft[sdx].wpfinal -local text -text=string.format("Flight %s passing waypoint #%d %s.",group:GetName(),wp,rat.waypointdescriptions[wp]) -BASE.T(rat,RAT.id..text) -local status=rat.waypointstatus[wp] -rat:_SetStatus(group,status) -if wp==WPholding then -text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.",group:GetName(),destination) -MESSAGE:New(text,10):ToAllIf(rat.reportstatus) -if rat.ATCswitch then -if rat.f10menu then -MENU_MISSION_COMMAND:New("Clear for landing",rat.Menu[rat.SubMenuName].groups[sdx],rat.ClearForLanding,rat,group:GetName()) -end -rat._ATCRegisterFlight(rat,group:GetName(),Tnow) -end -end -if wp==WPfinal then -text=string.format("Flight %s arrived at final destination %s.",group:GetName(),destination) -MESSAGE:New(text,10):ToAllIf(rat.reportstatus) -BASE.T(rat,RAT.id..text) -if landing==RAT.wp.air then -text=string.format("Activating despawn switch for flight %s! Group will be detroyed soon.",group:GetName()) -MESSAGE:New(text,10):ToAllIf(rat.Debug) -BASE.T(rat,RAT.id..text) -rat.ratcraft[sdx].despawnme=true -end -end -end -function RAT:_TaskFunction(FunctionString,...) -self:F2({FunctionString,arg}) -local DCSTask -local ArgumentKey -local templatename=self.templategroup:GetName() -local groupname=self:_AnticipatedGroupName() -local DCSScript={} -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: (.*)") -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))) -return DCSTask -end -function RAT:_AnticipatedGroupName(index) -local index=index or self.SpawnIndex+1 -return string.format("%s#%03d",self.alias,index) -end -function RAT:_ActivateUncontrolled() -self:F() -local idx={} -local rat={} -local nactive=0 -for spawnindex,ratcraft in pairs(self.ratcraft)do -local group=ratcraft.group -if group and group:IsAlive()then -local text=string.format("Uncontrolled: Group = %s (spawnindex = %d), active = %s.",ratcraft.group:GetName(),spawnindex,tostring(ratcraft.active)) -self:T2(RAT.id..text) -if ratcraft.active then -nactive=nactive+1 -else -table.insert(idx,spawnindex) -end -end -end -local text=string.format("Uncontrolled: Ninactive = %d, Nactive = %d (of max %d).",#idx,nactive,self.activate_max) -self:T(RAT.id..text) -if#idx>0 and nactive=1 then -for i=1,nunits do -table.insert(parkingspots,spots[1].Coordinate) -table.insert(parkingindex,spots[1].TerminalID) -end -PointVec3=spots[1].Coordinate -else -_notenough=true -end -elseif spawnonairport then -if nfree>=nunits then -for i=1,nunits do -table.insert(parkingspots,spots[i].Coordinate) -table.insert(parkingindex,spots[i].TerminalID) -end -else -_notenough=true -end -end -if _notenough then -if self.respawn_inair and not self.SpawnUnControlled then -self:E(RAT.id..string.format("WARNING: Group %s has no parking spots at %s ==> air start!",self.SpawnTemplatePrefix,departure:GetName())) -spawnonground=false -spawnonship=false -spawnonfarp=false -spawnonrunway=false -waypoints[1].type=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -waypoints[1].action=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -PointVec3.x=PointVec3.x+math.random(-1500,1500) -PointVec3.z=PointVec3.z+math.random(-1500,1500) -if self.category==RAT.cat.heli then -PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000) -else -PointVec3.y=PointVec3:GetLandHeight()+math.random(500,3000) -end -else -self:E(RAT.id..string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!",self.SpawnTemplatePrefix,departure:GetName())) -return nil -end -end -else -end -for UnitID=1,nunits do -local UnitTemplate=SpawnTemplate.units[UnitID] -local SX=UnitTemplate.x -local SY=UnitTemplate.y -local BX=SpawnTemplate.route.points[1].x -local BY=SpawnTemplate.route.points[1].y -local TX=PointVec3.x+(SX-BX) -local TY=PointVec3.z+(SY-BY) -if spawnonground then -if spawnonship or spawnonfarp or spawnonrunway or automatic then -self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.",self.alias,departure:GetName())) -SpawnTemplate.units[UnitID].x=PointVec3.x -SpawnTemplate.units[UnitID].y=PointVec3.z -SpawnTemplate.units[UnitID].alt=PointVec3.y -else -self:T(RAT.id..string.format("RAT group %s spawning at airbase %s on parking spot id %d",self.alias,departure:GetName(),parkingindex[UnitID])) -SpawnTemplate.units[UnitID].x=parkingspots[UnitID].x -SpawnTemplate.units[UnitID].y=parkingspots[UnitID].z -SpawnTemplate.units[UnitID].alt=parkingspots[UnitID].y -end -else -self:T(RAT.id..string.format("RAT group %s spawning in air at %s.",self.alias,departure:GetName())) -SpawnTemplate.units[UnitID].x=TX -SpawnTemplate.units[UnitID].y=TY -SpawnTemplate.units[UnitID].alt=PointVec3.y -end -if self.Debug then -local unitspawn=COORDINATE:New(SpawnTemplate.units[UnitID].x,SpawnTemplate.units[UnitID].alt,SpawnTemplate.units[UnitID].y) -unitspawn:MarkToAll(string.format("RAT %s Spawnplace unit #%d",self.alias,UnitID)) -end -UnitTemplate.parking=nil -UnitTemplate.parking_id=nil -if parkingindex[UnitID]and not automatic then -UnitTemplate.parking=parkingindex[UnitID] -end -self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking = %s",self.alias,UnitID,tostring(UnitTemplate.parking))) -self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias,UnitID,tostring(UnitTemplate.parking_id))) -SpawnTemplate.units[UnitID].heading=heading -SpawnTemplate.units[UnitID].psi=-heading -if livery then -SpawnTemplate.units[UnitID].livery_id=livery -end -if self.actype then -SpawnTemplate.units[UnitID]["type"]=self.actype -end -SpawnTemplate.units[UnitID]["skill"]=self.skill -if self.onboardnum then -SpawnTemplate.units[UnitID]["onboard_num"]=string.format("%s%d%02d",self.onboardnum,(self.SpawnIndex-1)%10,(self.onboardnum0-1)+UnitID) -end -SpawnTemplate.CoalitionID=self.coalition -if self.country then -SpawnTemplate.CountryID=self.country -end -end -for i,wp in ipairs(waypoints)do -SpawnTemplate.route.points[i]=wp -end -SpawnTemplate.x=PointVec3.x -SpawnTemplate.y=PointVec3.z -if self.radio then -SpawnTemplate.communication=self.radio -end -if self.frequency then -SpawnTemplate.frequency=self.frequency -end -if self.modulation then -SpawnTemplate.modulation=self.modulation -end -self:T(SpawnTemplate) -end -end -return true -end -function RAT:_ATCInit(airports_map) -if not RAT.ATC.init then -local text -text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay -BASE:T(RAT.id..text) -RAT.ATC.init=true -for _,ap in pairs(airports_map)do -local name=ap:GetName() -RAT.ATC.airport[name]={} -RAT.ATC.airport[name].queue={} -RAT.ATC.airport[name].busy=false -RAT.ATC.airport[name].onfinal={} -RAT.ATC.airport[name].Nonfinal=0 -RAT.ATC.airport[name].traffic=0 -RAT.ATC.airport[name].Tlastclearance=nil -end -SCHEDULER:New(nil,RAT._ATCCheck,{self},5,15) -SCHEDULER:New(nil,RAT._ATCStatus,{self},5,60) -RAT.ATC.T0=timer.getTime() -end -end -function RAT:_ATCAddFlight(name,dest) -BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.",RAT.id,dest,name,dest)) -RAT.ATC.flight[name]={} -RAT.ATC.flight[name].destination=dest -RAT.ATC.flight[name].Tarrive=-1 -RAT.ATC.flight[name].holding=-1 -RAT.ATC.flight[name].Tonfinal=-1 -end -function RAT:_ATCDelFlight(t,entry) -for k,_ in pairs(t)do -if k==entry then -t[entry]=nil -end -end -end -function RAT:_ATCRegisterFlight(name,time) -BASE:T(RAT.id.."Flight "..name.." registered at ATC for landing clearance.") -RAT.ATC.flight[name].Tarrive=time -RAT.ATC.flight[name].holding=0 -end -function RAT:_ATCStatus() -local Tnow=timer.getTime() -for name,_ in pairs(RAT.ATC.flight)do -local hold=RAT.ATC.flight[name].holding -local dest=RAT.ATC.flight[name].destination -if hold>=0 then -local busy="Runway state is unknown" -if RAT.ATC.airport[dest].Nonfinal>0 then -busy="Runway is occupied by "..RAT.ATC.airport[dest].Nonfinal -else -busy="Runway is currently clear" -end -local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.",dest,name,hold/60,hold%60,busy) -BASE:T(RAT.id..text) -elseif hold==RAT.ATC.onfinal then -local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal -local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.",dest,name,Tfinal/60,Tfinal%60) -BASE:T(RAT.id..text) -elseif hold==RAT.ATC.unregistered then -else -BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().") -end -end -end -function RAT:_ATCCheck() -RAT:_ATCQueue() -local Tnow=timer.getTime() -for name,_ in pairs(RAT.ATC.airport)do -for qID,flight in ipairs(RAT.ATC.airport[name].queue)do -local nqueue=#RAT.ATC.airport[name].queue -local landing1 -if RAT.ATC.airport[name].Tlastclearance then -landing1=(Tnow-RAT.ATC.airport[name].Tlastclearance>RAT.ATC.delay)and RAT.ATC.airport[name].Nonfinal=0 then -RAT.ATC.flight[name].holding=Tnow-RAT.ATC.flight[name].Tarrive -end -local hold=RAT.ATC.flight[name].holding -local dest=RAT.ATC.flight[name].destination -if hold>=0 and airport==dest then -_queue[#_queue+1]={name,hold} -end -end -local function compare(a,b) -return a[2]>b[2] -end -table.sort(_queue,compare) -RAT.ATC.airport[airport].queue={} -for k,v in ipairs(_queue)do -table.insert(RAT.ATC.airport[airport].queue,v[1]) -end -end -end -RATMANAGER={ -ClassName="RATMANAGER", -Debug=false, -rat={}, -name={}, -alive={}, -min={}, -nrat=0, -ntot=nil, -Tcheck=60, -dTspawn=1.0, -manager=nil, -managerid=nil, -} -RATMANAGER.id="RATMANAGER | " -function RATMANAGER:New(ntot) -local self=BASE:Inherit(self,BASE:New()) -self.ntot=ntot or 1 -self:E(RATMANAGER.id..string.format("Creating manager for %d groups.",ntot)) -return self -end -function RATMANAGER:Add(ratobject,min) -ratobject.norespawn=true -ratobject.f10menu=false -self.nrat=self.nrat+1 -self.rat[self.nrat]=ratobject -self.alive[self.nrat]=0 -self.name[self.nrat]=ratobject.alias -self.min[self.nrat]=min or 1 -self:T(RATMANAGER.id..string.format("Adding ratobject %s with min flights = %d",self.name[self.nrat],self.min[self.nrat])) -ratobject:Spawn(0) -return self -end -function RATMANAGER:Start(delay) -local delay=delay or 5 -local text=string.format(RATMANAGER.id.."RAT manager will be started in %d seconds.\n",delay) -text=text..string.format("Managed groups:\n") -for i=1,self.nrat do -text=text..string.format("- %s with min groups %d\n",self.name[i],self.min[i]) -end -text=text..string.format("Number of constantly alive groups %d",self.ntot) -self:E(text) -SCHEDULER:New(nil,self._Start,{self},delay) -return self -end -function RATMANAGER:_Start() -local n=0 -for i=1,self.nrat do -n=n+self.min[i] -end -self.ntot=math.max(self.ntot,n) -local N=self:_RollDice(self.nrat,self.ntot,self.min,self.alive) -local time=0.0 -for i=1,self.nrat do -for j=1,N[i]do -time=time+self.dTspawn -SCHEDULER:New(nil,RAT._SpawnWithRoute,{self.rat[i]},time) -end -end -for i=1,self.nrat do -if self.rat[i].uncontrolled and self.rat[i].activate_uncontrolled then -local Tactivate=math.max(time+1,self.rat[i].activate_delay) -SCHEDULER:New(self.rat[i],self.rat[i]._ActivateUncontrolled,{self.rat[i]},Tactivate,self.rat[i].activate_delta,self.rat[i].activate_frand) -end -end -local TstartManager=math.max(time+1,self.Tcheck) -self.manager,self.managerid=SCHEDULER:New(self,self._Manage,{self},TstartManager,self.Tcheck) -local text=string.format(RATMANAGER.id.."Starting RAT manager with scheduler ID %s in %d seconds. Repeat interval %d seconds.",self.managerid,TstartManager,self.Tcheck) -self:E(text) -return self -end -function RATMANAGER:Stop(delay) -delay=delay or 1 -self:E(string.format(RATMANAGER.id.."Manager will be stopped in %d seconds.",delay)) -SCHEDULER:New(nil,self._Stop,{self},delay) -return self -end -function RATMANAGER:_Stop() -self:E(string.format(RATMANAGER.id.."Stopping manager with scheduler ID %s.",self.managerid)) -self.manager:Stop(self.managerid) -return self -end -function RATMANAGER:SetTcheck(dt) -self.Tcheck=dt or 60 -return self -end -function RATMANAGER:SetTspawn(dt) -self.dTspawn=dt or 1.0 -return self -end -function RATMANAGER:_Manage() -local ntot=self:_Count() -local text=string.format("Number of alive groups %d. New groups to be spawned %d.",ntot,self.ntot-ntot) -self:T(RATMANAGER.id..text) -local N=self:_RollDice(self.nrat,self.ntot,self.min,self.alive) -local time=0.0 -for i=1,self.nrat do -for j=1,N[i]do -time=time+self.dTspawn -SCHEDULER:New(nil,RAT._SpawnWithRoute,{self.rat[i]},time) -end -end -end -function RATMANAGER:_Count() -local ntotal=0 -for i=1,self.nrat do -local n=0 -local ratobject=self.rat[i] -for spawnindex,ratcraft in pairs(ratobject.ratcraft)do -local group=ratcraft.group -if group and group:IsAlive()then -n=n+1 -end -end -self.alive[i]=n -ntotal=ntotal+n -local text=string.format("Number of alive groups of %s = %d",self.name[i],n) -self:T(RATMANAGER.id..text) -end -return ntotal -end -function RATMANAGER:_RollDice(nrat,ntot,min,alive) -local function sum(A,index) -local summe=0 -for _,i in ipairs(index)do -summe=summe+A[i] -end -return summe -end -local N={} -local M={} -local P={} -for i=1,nrat do -N[#N+1]=0 -M[#M+1]=math.max(alive[i],min[i]) -P[#P+1]=math.max(min[i]-alive[i],0) -end -local mini={} -local maxi={} -local rattab={} -for i=1,nrat do -table.insert(rattab,i) -end -local done={} -local nnew=ntot -for i=1,nrat do -nnew=nnew-alive[i] -end -for i=1,nrat-1 do -local r=math.random(#rattab) -local j=rattab[r] -table.remove(rattab,r) -table.insert(done,j) -local sN=sum(N,done) -local sP=sum(P,rattab) -maxi[j]=nnew-sN-sP -mini[j]=P[j] -if maxi[j]>=mini[j]then -N[j]=math.random(mini[j],maxi[j]) -else -N[j]=0 -end -self:T3(string.format("RATMANAGER: i=%d, alive=%d, min=%d, mini=%d, maxi=%d, add=%d, sumN=%d, sumP=%d",j,alive[j],min[j],mini[j],maxi[j],N[j],sN,sP)) -end -local j=rattab[1] -N[j]=nnew-sum(N,done) -mini[j]=nnew-sum(N,done) -maxi[j]=nnew-sum(N,done) -table.remove(rattab,1) -table.insert(done,j) -local text=RATMANAGER.id.."\n" -for i=1,nrat do -text=text..string.format("%s: i=%d, alive=%d, min=%d, mini=%d, maxi=%d, add=%d\n",self.name[i],i,alive[i],min[i],mini[i],maxi[i],N[i]) -end -text=text..string.format("Total # of groups to add = %d",sum(N,done)) -self:T(text) -return N -end -RANGE={ -ClassName="RANGE", -Debug=false, -verbose=0, -id=nil, -rangename=nil, -location=nil, -messages=true, -rangeradius=5000, -rangezone=nil, -strafeTargets={}, -bombingTargets={}, -nbombtargets=0, -nstrafetargets=0, -MenuAddedTo={}, -planes={}, -strafeStatus={}, -strafePlayerResults={}, -bombPlayerResults={}, -PlayerSettings={}, -dtBombtrack=0.005, -BombtrackThreshold=25000, -Tmsg=30, -examinergroupname=nil, -examinerexclusive=nil, -strafemaxalt=914, -ndisplayresult=10, -BombSmokeColor=SMOKECOLOR.Red, -StrafeSmokeColor=SMOKECOLOR.Green, -StrafePitSmokeColor=SMOKECOLOR.White, -illuminationminalt=500, -illuminationmaxalt=1000, -scorebombdistance=1000, -TdelaySmoke=3.0, -eventmoose=true, -trackbombs=true, -trackrockets=true, -trackmissiles=true, -defaultsmokebomb=true, -autosave=false, -instructorfreq=nil, -instructor=nil, -rangecontrolfreq=nil, -rangecontrol=nil, -soundpath="Range Soundfiles/" -} -RANGE.Defaults={ -goodhitrange=25, -strafemaxalt=914, -dtBombtrack=0.005, -Tmsg=30, -ndisplayresult=10, -rangeradius=5000, -TdelaySmoke=3.0, -boxlength=3000, -boxwidth=300, -goodpass=20, -goodhitrange=25, -foulline=610, -} -RANGE.TargetType={ -UNIT="Unit", -STATIC="Static", -COORD="Coordinate", -} -RANGE.Sound={ -RC0={filename="RC-0.ogg",duration=0.60}, -RC1={filename="RC-1.ogg",duration=0.47}, -RC2={filename="RC-2.ogg",duration=0.43}, -RC3={filename="RC-3.ogg",duration=0.50}, -RC4={filename="RC-4.ogg",duration=0.58}, -RC5={filename="RC-5.ogg",duration=0.54}, -RC6={filename="RC-6.ogg",duration=0.61}, -RC7={filename="RC-7.ogg",duration=0.53}, -RC8={filename="RC-8.ogg",duration=0.34}, -RC9={filename="RC-9.ogg",duration=0.54}, -RCAccuracy={filename="RC-Accuracy.ogg",duration=0.67}, -RCDegrees={filename="RC-Degrees.ogg",duration=0.59}, -RCExcellentHit={filename="RC-ExcellentHit.ogg",duration=0.76}, -RCExcellentPass={filename="RC-ExcellentPass.ogg",duration=0.89}, -RCFeet={filename="RC-Feet.ogg",duration=0.49}, -RCFor={filename="RC-For.ogg",duration=0.64}, -RCGoodHit={filename="RC-GoodHit.ogg",duration=0.52}, -RCGoodPass={filename="RC-GoodPass.ogg",duration=0.62}, -RCHitsOnTarget={filename="RC-HitsOnTarget.ogg",duration=0.88}, -RCImpact={filename="RC-Impact.ogg",duration=0.61}, -RCIneffectiveHit={filename="RC-IneffectiveHit.ogg",duration=0.86}, -RCIneffectivePass={filename="RC-IneffectivePass.ogg",duration=0.99}, -RCInvalidHit={filename="RC-InvalidHit.ogg",duration=2.97}, -RCLeftStrafePitTooQuickly={filename="RC-LeftStrafePitTooQuickly.ogg",duration=3.09}, -RCPercent={filename="RC-Percent.ogg",duration=0.56}, -RCPoorHit={filename="RC-PoorHit.ogg",duration=0.54}, -RCPoorPass={filename="RC-PoorPass.ogg",duration=0.68}, -RCRollingInOnStrafeTarget={filename="RC-RollingInOnStrafeTarget.ogg",duration=1.38}, -RCTotalRoundsFired={filename="RC-TotalRoundsFired.ogg",duration=1.22}, -RCWeaponImpactedTooFar={filename="RC-WeaponImpactedTooFar.ogg",duration=3.73}, -IR0={filename="IR-0.ogg",duration=0.55}, -IR1={filename="IR-1.ogg",duration=0.41}, -IR2={filename="IR-2.ogg",duration=0.37}, -IR3={filename="IR-3.ogg",duration=0.41}, -IR4={filename="IR-4.ogg",duration=0.37}, -IR5={filename="IR-5.ogg",duration=0.43}, -IR6={filename="IR-6.ogg",duration=0.55}, -IR7={filename="IR-7.ogg",duration=0.43}, -IR8={filename="IR-8.ogg",duration=0.38}, -IR9={filename="IR-9.ogg",duration=0.55}, -IRDecimal={filename="IR-Decimal.ogg",duration=0.54}, -IRMegaHertz={filename="IR-MegaHertz.ogg",duration=0.87}, -IREnterRange={filename="IR-EnterRange.ogg",duration=4.83}, -IRExitRange={filename="IR-ExitRange.ogg",duration=3.10}, -} -RANGE.Names={} -RANGE.MenuF10={} -RANGE.MenuF10Root=nil -RANGE.version="2.3.0" -function RANGE:New(rangename) -BASE:F({rangename=rangename}) -local self=BASE:Inherit(self,FSM:New()) -self.rangename=rangename or"Practice Range" -self.id=string.format("RANGE %s | ",self.rangename) -local text=string.format("Script version %s - creating new RANGE object %s.",RANGE.version,self.rangename) -self:I(self.id..text) -self:SetDefaultPlayerSmokeBomb() -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Running") -self:AddTransition("*","Status","*") -self:AddTransition("*","Impact","*") -self:AddTransition("*","EnterRange","*") -self:AddTransition("*","ExitRange","*") -self:AddTransition("*","Save","*") -self:AddTransition("*","Load","*") -return self -end -function RANGE:onafterStart() -local _location=nil -local _count=0 -for _,_target in pairs(self.bombingTargets)do -_count=_count+1 -if _location==nil then -_location=self:_GetBombTargetCoordinate(_target) -end -end -self.nbombtargets=_count -_count=0 -for _,_target in pairs(self.strafeTargets)do -_count=_count+1 -for _,_unit in pairs(_target.targets)do -if _location==nil then -_location=_unit:GetCoordinate() -end -end -end -self.nstrafetargets=_count -if self.location==nil then -self.location=_location -end -if self.location==nil then -local text=string.format("ERROR! No range location found. Number of strafe targets = %d. Number of bomb targets = %d.",self.nstrafetargets,self.nbombtargets) -self:E(self.id..text) -return -end -if self.rangezone==nil then -self.rangezone=ZONE_RADIUS:New(self.rangename,{x=self.location.x,y=self.location.z},self.rangeradius) -end -local text=string.format("Starting RANGE %s. Number of strafe targets = %d. Number of bomb targets = %d.",self.rangename,self.nstrafetargets,self.nbombtargets) -self:I(self.id..text) -if self.eventmoose then -self:T(self.id.."Events are handled by MOOSE.") -self:HandleEvent(EVENTS.Birth) -self:HandleEvent(EVENTS.Hit) -self:HandleEvent(EVENTS.Shot) -else -self:T(self.id.."Events are handled directly by DCS.") -world.addEventHandler(self) -end -for _,_target in pairs(self.bombingTargets)do -local _static=_target.type==RANGE.TargetType.STATIC -if _target.move and _static==false and _target.speed>1 then -local unit=_target.target -_target.target:PatrolZones({self.rangezone},_target.speed*0.75,"Off road") -end -end -if self.rangecontrolfreq then -self.rangecontrol=RADIOQUEUE:New(self.rangecontrolfreq,nil,self.rangename) -self.rangecontrol.schedonce=true -self.rangecontrol:SetDigit(0,RANGE.Sound.RC0.filename,RANGE.Sound.RC0.duration,self.soundpath) -self.rangecontrol:SetDigit(1,RANGE.Sound.RC1.filename,RANGE.Sound.RC1.duration,self.soundpath) -self.rangecontrol:SetDigit(2,RANGE.Sound.RC2.filename,RANGE.Sound.RC2.duration,self.soundpath) -self.rangecontrol:SetDigit(3,RANGE.Sound.RC3.filename,RANGE.Sound.RC3.duration,self.soundpath) -self.rangecontrol:SetDigit(4,RANGE.Sound.RC4.filename,RANGE.Sound.RC4.duration,self.soundpath) -self.rangecontrol:SetDigit(5,RANGE.Sound.RC5.filename,RANGE.Sound.RC5.duration,self.soundpath) -self.rangecontrol:SetDigit(6,RANGE.Sound.RC6.filename,RANGE.Sound.RC6.duration,self.soundpath) -self.rangecontrol:SetDigit(7,RANGE.Sound.RC7.filename,RANGE.Sound.RC7.duration,self.soundpath) -self.rangecontrol:SetDigit(8,RANGE.Sound.RC8.filename,RANGE.Sound.RC8.duration,self.soundpath) -self.rangecontrol:SetDigit(9,RANGE.Sound.RC9.filename,RANGE.Sound.RC9.duration,self.soundpath) -self.rangecontrol:SetSenderCoordinate(self.location) -self.rangecontrol:SetSenderUnitName(self.rangecontrolrelayname) -self.rangecontrol:Start(1,0.1) -if self.instructorfreq then -self.instructor=RADIOQUEUE:New(self.instructorfreq,nil,self.rangename) -self.instructor.schedonce=true -self.instructor:SetDigit(0,RANGE.Sound.IR0.filename,RANGE.Sound.IR0.duration,self.soundpath) -self.instructor:SetDigit(1,RANGE.Sound.IR1.filename,RANGE.Sound.IR1.duration,self.soundpath) -self.instructor:SetDigit(2,RANGE.Sound.IR2.filename,RANGE.Sound.IR2.duration,self.soundpath) -self.instructor:SetDigit(3,RANGE.Sound.IR3.filename,RANGE.Sound.IR3.duration,self.soundpath) -self.instructor:SetDigit(4,RANGE.Sound.IR4.filename,RANGE.Sound.IR4.duration,self.soundpath) -self.instructor:SetDigit(5,RANGE.Sound.IR5.filename,RANGE.Sound.IR5.duration,self.soundpath) -self.instructor:SetDigit(6,RANGE.Sound.IR6.filename,RANGE.Sound.IR6.duration,self.soundpath) -self.instructor:SetDigit(7,RANGE.Sound.IR7.filename,RANGE.Sound.IR7.duration,self.soundpath) -self.instructor:SetDigit(8,RANGE.Sound.IR8.filename,RANGE.Sound.IR8.duration,self.soundpath) -self.instructor:SetDigit(9,RANGE.Sound.IR9.filename,RANGE.Sound.IR9.duration,self.soundpath) -self.instructor:SetSenderCoordinate(self.location) -self.instructor:SetSenderUnitName(self.instructorrelayname) -self.instructor:Start(1,0.1) -end -end -if self.autosave then -self:Load() -end -if self.Debug then -self:_MarkTargetsOnMap() -self:_SmokeBombTargets() -self:_SmokeStrafeTargets() -self:_SmokeStrafeTargetBoxes() -self.rangezone:SmokeZone(SMOKECOLOR.White) -end -self:__Status(-60) -end -function RANGE:SetMaxStrafeAlt(maxalt) -self.strafemaxalt=maxalt or RANGE.Defaults.strafemaxalt -return self -end -function RANGE:SetBombtrackTimestep(dt) -self.dtBombtrack=dt or RANGE.Defaults.dtBombtrack -return self -end -function RANGE:SetMessageTimeDuration(time) -self.Tmsg=time or RANGE.Defaults.Tmsg -return self -end -function RANGE:SetAutosaveOn() -self.autosave=true -return self -end -function RANGE:SetAutosaveOff() -self.autosave=false -return self -end -function RANGE:SetMessageToExaminer(examinergroupname,exclusively) -self.examinergroupname=examinergroupname -self.examinerexclusive=exclusively -return self -end -function RANGE:SetDisplayedMaxPlayerResults(nmax) -self.ndisplayresult=nmax or RANGE.Defaults.ndisplayresult -return self -end -function RANGE:SetRangeRadius(radius) -self.rangeradius=radius*1000 or RANGE.Defaults.rangeradius -return self -end -function RANGE:SetDefaultPlayerSmokeBomb(switch) -if switch==true or switch==nil then -self.defaultsmokebomb=true -else -self.defaultsmokebomb=false -end -return self -end -function RANGE:SetBombtrackThreshold(distance) -self.BombtrackThreshold=(distance or 25)*1000 -return self -end -function RANGE:SetRangeLocation(coordinate) -self.location=coordinate -return self -end -function RANGE:SetRangeZone(zone) -self.rangezone=zone -return self -end -function RANGE:SetBombTargetSmokeColor(colorid) -self.BombSmokeColor=colorid or SMOKECOLOR.Red -return self -end -function RANGE:SetScoreBombDistance(distance) -self.scorebombdistance=distance or 1000 -return self -end -function RANGE:SetStrafeTargetSmokeColor(colorid) -self.StrafeSmokeColor=colorid or SMOKECOLOR.Green -return self -end -function RANGE:SetStrafePitSmokeColor(colorid) -self.StrafePitSmokeColor=colorid or SMOKECOLOR.White -return self -end -function RANGE:SetSmokeTimeDelay(delay) -self.TdelaySmoke=delay or RANGE.Defaults.TdelaySmoke -return self -end -function RANGE:DebugON() -self.Debug=true -return self -end -function RANGE:DebugOFF() -self.Debug=false -return self -end -function RANGE:SetMessagesOFF() -self.messages=false -return self -end -function RANGE:SetMessagesON() -self.messages=true -return self -end -function RANGE:TrackBombsON() -self.trackbombs=true -return self -end -function RANGE:TrackBombsOFF() -self.trackbombs=false -return self -end -function RANGE:TrackRocketsON() -self.trackrockets=true -return self -end -function RANGE:TrackRocketsOFF() -self.trackrockets=false -return self -end -function RANGE:TrackMissilesON() -self.trackmissiles=true -return self -end -function RANGE:TrackMissilesOFF() -self.trackmissiles=false -return self -end -function RANGE:SetRangeControl(frequency,relayunitname) -self.rangecontrolfreq=frequency or 256 -self.rangecontrolrelayname=relayunitname -return self -end -function RANGE:SetInstructorRadio(frequency,relayunitname) -self.instructorfreq=frequency or 305 -self.instructorrelayname=relayunitname -return self -end -function RANGE:SetSoundfilesPath(path) -self.soundpath=tostring(path or"Range Soundfiles/") -self:I(self.id..string.format("Setting sound files path to %s",self.soundpath)) -return self -end -function RANGE:AddStrafePit(targetnames,boxlength,boxwidth,heading,inverseheading,goodpass,foulline) -self:F({targetnames=targetnames,boxlength=boxlength,boxwidth=boxwidth,heading=heading,inverseheading=inverseheading,goodpass=goodpass,foulline=foulline}) -if type(targetnames)~="table"then -targetnames={targetnames} -end -local _targets={} -local center=nil -local ntargets=0 -for _i,_name in ipairs(targetnames)do -local _isstatic=self:_CheckStatic(_name) -local unit=nil -if _isstatic==true then -self:T(self.id..string.format("Adding STATIC object %s as strafe target #%d.",_name,_i)) -unit=STATIC:FindByName(_name,false) -elseif _isstatic==false then -self:T(self.id..string.format("Adding UNIT object %s as strafe target #%d.",_name,_i)) -unit=UNIT:FindByName(_name) -else -local text=string.format("ERROR! Could not find ANY strafe target object with name %s.",_name) -self:E(self.id..text) -end -if unit then -table.insert(_targets,unit) -if center==nil then -center=unit -end -ntargets=ntargets+1 -end -end -if ntargets==0 then -local text=string.format("ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s",self.rangename) -self:E(self.id..text) -return -end -local l=boxlength or RANGE.Defaults.boxlength -local w=(boxwidth or RANGE.Defaults.boxwidth)/2 -local heading=heading or center:GetHeading() -if inverseheading~=nil then -if inverseheading then -heading=heading-180 -end -end -if heading<0 then -heading=heading+360 -end -if heading>360 then -heading=heading-360 -end -goodpass=goodpass or RANGE.Defaults.goodpass -foulline=foulline or RANGE.Defaults.foulline -local Ccenter=center:GetCoordinate() -local _name=center:GetName() -local p={} -p[#p+1]=Ccenter:Translate(w,heading+90) -p[#p+1]=p[#p]:Translate(l,heading) -p[#p+1]=p[#p]:Translate(2*w,heading-90) -p[#p+1]=p[#p]:Translate(-l,heading) -local pv2={} -for i,p in ipairs(p)do -pv2[i]={x=p.x,y=p.z} -end -local _polygon=ZONE_POLYGON_BASE:New(_name,pv2) -local st={} -st.name=_name -st.polygon=_polygon -st.coordinate=Ccenter -st.goodPass=goodpass -st.targets=_targets -st.foulline=foulline -st.smokepoints=p -st.heading=heading -table.insert(self.strafeTargets,st) -local text=string.format("Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f",_name,ntargets,heading,l,w,goodpass,foulline) -self:T(self.id..text) -return self -end -function RANGE:AddStrafePitGroup(group,boxlength,boxwidth,heading,inverseheading,goodpass,foulline) -self:F({group=group,boxlength=boxlength,boxwidth=boxwidth,heading=heading,inverseheading=inverseheading,goodpass=goodpass,foulline=foulline}) -if group and group:IsAlive()then -local _units=group:GetUnits() -local _names={} -for _,_unit in ipairs(_units)do -local _unit=_unit -if _unit and _unit:IsAlive()then -local _name=_unit:GetName() -table.insert(_names,_name) -end -end -self:AddStrafePit(_names,boxlength,boxwidth,heading,inverseheading,goodpass,foulline) -end -return self -end -function RANGE:AddBombingTargets(targetnames,goodhitrange,randommove) -self:F({targetnames=targetnames,goodhitrange=goodhitrange,randommove=randommove}) -if type(targetnames)~="table"then -targetnames={targetnames} -end -goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange -for _,name in pairs(targetnames)do -local _isstatic=self:_CheckStatic(name) -if _isstatic==true then -local _static=STATIC:FindByName(name) -self:T2(self.id..string.format("Adding static bombing target %s with hit range %d.",name,goodhitrange,false)) -self:AddBombingTargetUnit(_static,goodhitrange) -elseif _isstatic==false then -local _unit=UNIT:FindByName(name) -self:T2(self.id..string.format("Adding unit bombing target %s with hit range %d.",name,goodhitrange,randommove)) -self:AddBombingTargetUnit(_unit,goodhitrange) -else -self:E(self.id..string.format("ERROR! Could not find bombing target %s.",name)) -end -end -return self -end -function RANGE:AddBombingTargetUnit(unit,goodhitrange,randommove) -self:F({unit=unit,goodhitrange=goodhitrange,randommove=randommove}) -local name=unit:GetName() -local _isstatic=self:_CheckStatic(name) -goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange -if randommove==nil or _isstatic==true then -randommove=false -end -if _isstatic==true then -self:I(self.id..string.format("Adding STATIC bombing target %s with good hit range %d. Random move = %s.",name,goodhitrange,tostring(randommove))) -elseif _isstatic==false then -self:I(self.id..string.format("Adding UNIT bombing target %s with good hit range %d. Random move = %s.",name,goodhitrange,tostring(randommove))) -else -self:E(self.id..string.format("ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!",name)) -end -local speed=0 -if _isstatic==false then -speed=self:_GetSpeed(unit) -end -local target={} -target.name=name -target.target=unit -target.goodhitrange=goodhitrange -target.move=randommove -target.speed=speed -target.coordinate=unit:GetCoordinate() -if _isstatic then -target.type=RANGE.TargetType.STATIC -else -target.type=RANGE.TargetType.UNIT -end -table.insert(self.bombingTargets,target) -return self -end -function RANGE:AddBombingTargetCoordinate(coord,name,goodhitrange) -local target={} -target.name=name or"Bomb Target" -target.target=nil -target.goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange -target.move=false -target.speed=0 -target.coordinate=coord -target.type=RANGE.TargetType.COORD -table.insert(self.bombingTargets,target) -return self -end -function RANGE:AddBombingTargetGroup(group,goodhitrange,randommove) -self:F({group=group,goodhitrange=goodhitrange,randommove=randommove}) -if group then -local _units=group:GetUnits() -for _,_unit in pairs(_units)do -if _unit and _unit:IsAlive()then -self:AddBombingTargetUnit(_unit,goodhitrange,randommove) -end -end -end -return self -end -function RANGE:GetFoullineDistance(namepit,namefoulline) -self:F({namepit=namepit,namefoulline=namefoulline}) -local _staticpit=self:_CheckStatic(namepit) -local _staticfoul=self:_CheckStatic(namefoulline) -local pit=nil -if _staticpit==true then -pit=STATIC:FindByName(namepit,false) -elseif _staticpit==false then -pit=UNIT:FindByName(namepit) -else -self:E(self.id..string.format("ERROR! Pit object %s could not be found in GetFoullineDistance function. Check the name in the ME.",namepit)) -end -local foul=nil -if _staticfoul==true then -foul=STATIC:FindByName(namefoulline,false) -elseif _staticfoul==false then -foul=UNIT:FindByName(namefoulline) -else -self:E(self.id..string.format("ERROR! Foul line object %s could not be found in GetFoullineDistance function. Check the name in the ME.",namefoulline)) -end -local fouldist=0 -if pit~=nil and foul~=nil then -fouldist=pit:GetCoordinate():Get2DDistance(foul:GetCoordinate()) -else -self:E(self.id..string.format("ERROR! Foul line distance could not be determined. Check pit object name %s and foul line object name %s in the ME.",namepit,namefoulline)) -end -self:T(self.id..string.format("Foul line distance = %.1f m.",fouldist)) -return fouldist -end -function RANGE:onEvent(Event) -self:F3(Event) -if Event==nil or Event.initiator==nil then -self:T3("Skipping onEvent. Event or Event.initiator unknown.") -return true -end -if Unit.getByName(Event.initiator:getName())==nil then -self:T3("Skipping onEvent. Initiator unit name unknown.") -return true -end -local DCSiniunit=Event.initiator -local DCStgtunit=Event.target -local DCSweapon=Event.weapon -local EventData={} -local _playerunit=nil -local _playername=nil -if Event.initiator then -EventData.IniUnitName=Event.initiator:getName() -EventData.IniDCSGroup=Event.initiator:getGroup() -EventData.IniGroupName=Event.initiator:getGroup():getName() -_playerunit,_playername=self:_GetPlayerUnitAndName(EventData.IniUnitName) -end -if Event.target then -EventData.TgtUnitName=Event.target:getName() -EventData.TgtUnit=UNIT:FindByName(EventData.TgtUnitName) -end -if Event.weapon then -EventData.Weapon=Event.weapon -EventData.weapon=Event.weapon -EventData.WeaponTypeName=Event.weapon:getTypeName() -end -self:T3(self.id..string.format("EVENT: Event in onEvent with ID = %s",tostring(Event.id))) -self:T3(self.id..string.format("EVENT: Ini unit = %s",tostring(EventData.IniUnitName))) -self:T3(self.id..string.format("EVENT: Ini group = %s",tostring(EventData.IniGroupName))) -self:T3(self.id..string.format("EVENT: Ini player = %s",tostring(_playername))) -self:T3(self.id..string.format("EVENT: Tgt unit = %s",tostring(EventData.TgtUnitName))) -self:T3(self.id..string.format("EVENT: Wpn type = %s",tostring(EventData.WeaponTypeName))) -if Event.id==world.event.S_EVENT_BIRTH and _playername then -self:OnEventBirth(EventData) -end -if Event.id==world.event.S_EVENT_SHOT and _playername and Event.weapon then -self:OnEventShot(EventData) -end -if Event.id==world.event.S_EVENT_HIT and _playername and DCStgtunit then -self:OnEventHit(EventData) -end -end -function RANGE:OnEventBirth(EventData) -self:F({eventbirth=EventData}) -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T3(self.id.."BIRTH: unit = "..tostring(EventData.IniUnitName)) -self:T3(self.id.."BIRTH: group = "..tostring(EventData.IniGroupName)) -self:T3(self.id.."BIRTH: player = "..tostring(_playername)) -if _unit and _playername then -local _uid=_unit:GetID() -local _group=_unit:GetGroup() -local _gid=_group:GetID() -local _callsign=_unit:GetCallsign() -local text=string.format("Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)",_playername,_callsign,_unitName,_uid,_group:GetName(),_gid) -self:T(self.id..text) -self.strafeStatus[_uid]=nil -self:ScheduleOnce(0.1,self._AddF10Commands,self,_unitName) -self.PlayerSettings[_playername]={} -self.PlayerSettings[_playername].smokebombimpact=self.defaultsmokebomb -self.PlayerSettings[_playername].flaredirecthits=false -self.PlayerSettings[_playername].smokecolor=SMOKECOLOR.Blue -self.PlayerSettings[_playername].flarecolor=FLARECOLOR.Red -self.PlayerSettings[_playername].delaysmoke=true -self.PlayerSettings[_playername].messages=true -self.PlayerSettings[_playername].client=CLIENT:FindByName(_unitName,nil,true) -self.PlayerSettings[_playername].unitname=_unitName -self.PlayerSettings[_playername].playername=_playername -self.PlayerSettings[_playername].airframe=EventData.IniUnit:GetTypeName() -self.PlayerSettings[_playername].inzone=false -if self.planes[_uid]~=true then -self.timerCheckZone=TIMER:New(self._CheckInZone,self,EventData.IniUnitName):Start(1,1) -self.planes[_uid]=true -end -end -end -function RANGE:OnEventHit(EventData) -self:F({eventhit=EventData}) -self:T3(self.id.."HIT: Ini unit = "..tostring(EventData.IniUnitName)) -self:T3(self.id.."HIT: Ini group = "..tostring(EventData.IniGroupName)) -self:T3(self.id.."HIT: Tgt target = "..tostring(EventData.TgtUnitName)) -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit==nil or _playername==nil then -return -end -local _unitID=_unit:GetID() -local target=EventData.TgtUnit -local targetname=EventData.TgtUnitName -local _currentTarget=self.strafeStatus[_unitID] -if _currentTarget and target:IsAlive()then -local playerPos=_unit:GetCoordinate() -local targetPos=target:GetCoordinate() -for _,_target in pairs(_currentTarget.zone.targets)do -if _target and _target:IsAlive()and _target:GetName()==targetname then -local dist=playerPos:Get2DDistance(targetPos) -if dist>_currentTarget.zone.foulline then -_currentTarget.hits=_currentTarget.hits+1 -if _unit and _playername and self.PlayerSettings[_playername].flaredirecthits then -targetPos:Flare(self.PlayerSettings[_playername].flarecolor) -end -else -if _currentTarget.pastfoulline==false and _unit and _playername then -local _d=_currentTarget.zone.foulline -local text=string.format("%s, Invalid hit!\nYou already passed foul line distance of %d m for target %s.",self:_myname(_unitName),_d,targetname) -self:_DisplayMessageToGroup(_unit,text) -self:T2(self.id..text) -_currentTarget.pastfoulline=true -end -end -end -end -end -for _,_bombtarget in pairs(self.bombingTargets)do -local _target=_bombtarget.target -if _target and _target:IsAlive()and _bombtarget.name==targetname then -if _unit and _playername then -if self.PlayerSettings[_playername].flaredirecthits then -local targetPos=_target:GetCoordinate() -targetPos:Flare(self.PlayerSettings[_playername].flarecolor) -end -end -end -end -end -function RANGE:OnEventShot(EventData) -self:F({eventshot=EventData}) -if EventData.Weapon==nil then -return -end -if EventData.IniDCSUnit==nil then -return -end -local _weapon=EventData.Weapon:getTypeName() -local _weaponStrArray=UTILS.Split(_weapon,"%.") -local _weaponName=_weaponStrArray[#_weaponStrArray] -local desc=EventData.Weapon:getDesc() -local weaponcategory=desc.category -self:T(self.id.."EVENT SHOT: Range "..self.rangename) -self:T(self.id.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) -self:T(self.id.."EVENT SHOT: Ini group = "..EventData.IniGroupName) -self:T(self.id.."EVENT SHOT: Weapon type = ".._weapon) -self:T(self.id.."EVENT SHOT: Weapon name = ".._weaponName) -self:T(self.id.."EVENT SHOT: Weapon cate = "..weaponcategory) -local _bombs=weaponcategory==Weapon.Category.BOMB -local _rockets=weaponcategory==Weapon.Category.ROCKET -local _missiles=weaponcategory==Weapon.Category.MISSILE -local _track=(_bombs and self.trackbombs)or(_rockets and self.trackrockets)or(_missiles and self.trackmissiles) -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -local dPR=self.BombtrackThreshold*2 -if _unit and _playername then -dPR=_unit:GetCoordinate():Get2DDistance(self.location) -self:T(self.id..string.format("Range %s, player %s, player-range distance = %d km.",self.rangename,_playername,dPR/1000)) -end -if _track and dPR<=self.BombtrackThreshold and _unit and _playername then -local playerData=self.PlayerSettings[_playername] -self:T(self.id..string.format("RANGE %s: Tracking %s - %s.",self.rangename,_weapon,EventData.weapon:getName())) -local _lastBombPos={x=0,y=0,z=0} -local function trackBomb(_ordnance) -local _status,_bombPos=pcall( -function() -return _ordnance:getPoint() -end) -self:T2(self.id..string.format("Range %s: Bomb still in air: %s",self.rangename,tostring(_status))) -if _status then -_lastBombPos={x=_bombPos.x,y=_bombPos.y,z=_bombPos.z} -return timer.getTime()+self.dtBombtrack -else -local _closetTarget=nil -local _distance=nil -local _closeCoord=nil -local _hitquality="POOR" -local _callsign=self:_myname(_unitName) -local impactcoord=COORDINATE:NewFromVec3(_lastBombPos) -local insidezone=self.rangezone:IsCoordinateInZone(impactcoord) -if self.Debug then -impactcoord:MarkToAll("Bomb impact point") -end -if playerData.smokebombimpact and insidezone then -if playerData.delaysmoke then -timer.scheduleFunction(self._DelayedSmoke,{coord=impactcoord,color=playerData.smokecolor},timer.getTime()+self.TdelaySmoke) -else -impactcoord:Smoke(playerData.smokecolor) -end -end -for _,_bombtarget in pairs(self.bombingTargets)do -local targetcoord=self:_GetBombTargetCoordinate(_bombtarget) -if targetcoord then -local _temp=impactcoord:Get2DDistance(targetcoord) -if _distance==nil or _temp<_distance then -_distance=_temp -_closetTarget=_bombtarget -_closeCoord=targetcoord -if _distance<=0.5*_bombtarget.goodhitrange then -_hitquality="EXCELLENT" -elseif _distance<=_bombtarget.goodhitrange then -_hitquality="GOOD" -elseif _distance<=2*_bombtarget.goodhitrange then -_hitquality="INEFFECTIVE" -else -_hitquality="POOR" -end -end -end -end -if _distance and _distance<=self.scorebombdistance then -if not self.bombPlayerResults[_playername]then -self.bombPlayerResults[_playername]={} -end -local _results=self.bombPlayerResults[_playername] -local result={} -result.name=_closetTarget.name or"unknown" -result.distance=_distance -result.radial=_closeCoord:HeadingTo(impactcoord) -result.weapon=_weaponName or"unknown" -result.quality=_hitquality -result.player=playerData.playername -result.time=timer.getAbsTime() -result.airframe=playerData.airframe -table.insert(_results,result) -self:Impact(result,playerData) -elseif insidezone then -local _message=string.format("%s, weapon impacted too far from nearest range target (>%.1f km). No score!",_callsign,self.scorebombdistance/1000) -self:_DisplayMessageToGroup(_unit,_message,nil,false) -if self.rangecontrol then -self.rangecontrol:NewTransmission(RANGE.Sound.RCWeaponImpactedTooFar.filename,RANGE.Sound.RCWeaponImpactedTooFar.duration,self.soundpath,nil,nil,_message,self.subduration) -end -else -self:T(self.id.."Weapon impacted outside range zone.") -end -self:T(self.id..string.format("Range %s, player %s: Terminating bomb track timer.",self.rangename,_playername)) -return nil -end -end -self:T(self.id..string.format("Range %s, player %s: Tracking of weapon starts in 0.1 seconds.",self.rangename,_playername)) -timer.scheduleFunction(trackBomb,EventData.weapon,timer.getTime()+0.1) -end -end -function RANGE:onafterStatus(From,Event,To) -if self.verbose>0 then -local fsmstate=self:GetState() -local text=string.format("Range status: %s",fsmstate) -if self.instructor then -local alive="N/A" -if self.instructorrelayname then -local relay=UNIT:FindByName(self.instructorrelayname) -if relay then -alive=tostring(relay:IsAlive()) -end -end -text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)",self.instructorfreq,tostring(self.instructorrelayname),alive) -end -if self.rangecontrol then -local alive="N/A" -if self.rangecontrolrelayname then -local relay=UNIT:FindByName(self.rangecontrolrelayname) -if relay then -alive=tostring(relay:IsAlive()) -end -end -text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)",self.rangecontrolfreq,tostring(self.rangecontrolrelayname),alive) -end -self:I(self.id..text) -end -self:_CheckPlayers() -self:__Status(-10) -end -function RANGE:onafterEnterRange(From,Event,To,player) -if self.instructor and self.rangecontrol then -local RF=UTILS.Split(string.format("%.3f",self.rangecontrolfreq),".") -self.instructor:NewTransmission(RANGE.Sound.IREnterRange.filename,RANGE.Sound.IREnterRange.duration,self.soundpath) -self.instructor:Number2Transmission(RF[1]) -if tonumber(RF[2])>0 then -self.instructor:NewTransmission(RANGE.Sound.IRDecimal.filename,RANGE.Sound.IRDecimal.duration,self.soundpath) -self.instructor:Number2Transmission(RF[2]) -end -self.instructor:NewTransmission(RANGE.Sound.IRMegaHertz.filename,RANGE.Sound.IRMegaHertz.duration,self.soundpath) -end -end -function RANGE:onafterExitRange(From,Event,To,player) -if self.instructor then -self.instructor:NewTransmission(RANGE.Sound.IRExitRange.filename,RANGE.Sound.IRExitRange.duration,self.soundpath) -end -end -function RANGE:onafterImpact(From,Event,To,result,player) -local targetname=nil -if#self.bombingTargets>1 then -local targetname=result.name -end -local text=string.format("%s, impact %03d° for %d ft",player.playername,result.radial,UTILS.MetersToFeet(result.distance)) -if targetname then -text=text..string.format(" from bulls of target %s.") -else -text=text.."." -end -text=text..string.format(" %s hit.",result.quality) -if self.rangecontrol then -self.rangecontrol:NewTransmission(RANGE.Sound.RCImpact.filename,RANGE.Sound.RCImpact.duration,self.soundpath,nil,nil,text,self.subduration) -self.rangecontrol:Number2Transmission(string.format("%03d",result.radial),nil,0.1) -self.rangecontrol:NewTransmission(RANGE.Sound.RCDegrees.filename,RANGE.Sound.RCDegrees.duration,self.soundpath) -self.rangecontrol:NewTransmission(RANGE.Sound.RCFor.filename,RANGE.Sound.RCFor.duration,self.soundpath) -self.rangecontrol:Number2Transmission(string.format("%d",UTILS.MetersToFeet(result.distance))) -self.rangecontrol:NewTransmission(RANGE.Sound.RCFeet.filename,RANGE.Sound.RCFeet.duration,self.soundpath) -if result.quality=="POOR"then -self.rangecontrol:NewTransmission(RANGE.Sound.RCPoorHit.filename,RANGE.Sound.RCPoorHit.duration,self.soundpath,nil,0.5) -elseif result.quality=="INEFFECTIVE"then -self.rangecontrol:NewTransmission(RANGE.Sound.RCIneffectiveHit.filename,RANGE.Sound.RCIneffectiveHit.duration,self.soundpath,nil,0.5) -elseif result.quality=="GOOD"then -self.rangecontrol:NewTransmission(RANGE.Sound.RCGoodHit.filename,RANGE.Sound.RCGoodHit.duration,self.soundpath,nil,0.5) -elseif result.quality=="EXCELLENT"then -self.rangecontrol:NewTransmission(RANGE.Sound.RCExcellentHit.filename,RANGE.Sound.RCExcellentHit.duration,self.soundpath,nil,0.5) -end -end -local unit=UNIT:FindByName(player.unitname) -self:_DisplayMessageToGroup(unit,text,nil,true) -self:T(self.id..text) -if self.autosave then -self:Save() -end -end -function RANGE:onbeforeSave(From,Event,To) -if io and lfs then -return true -else -self:E(self.id..string.format("WARNING: io and/or lfs not desanitized. Cannot save player results.")) -return false -end -end -function RANGE:onafterSave(From,Event,To) -local function _savefile(filename,data) -local f=io.open(filename,"wb") -if f then -f:write(data) -f:close() -self:I(self.id..string.format("Saving player results to file %s",tostring(filename))) -else -self:E(self.id..string.format("ERROR: Could not save results to file %s",tostring(filename))) -end -end -local path=lfs.writedir()..[[Logs\]] -local filename=path..string.format("RANGE-%s_BombingResults.csv",self.rangename) -local scores="Name,Pass,Target,Distance,Radial,Quality,Weapon,Airframe,Mission Time" -for playername,results in pairs(self.bombPlayerResults)do -for i,_result in pairs(results)do -local result=_result -local distance=result.distance -local weapon=result.weapon -local target=result.name -local radial=result.radial -local quality=result.quality -local time=UTILS.SecondsToClock(result.time) -local airframe=result.airframe -local date="n/a" -if os then -date=os.date() -end -scores=scores..string.format("\n%s,%d,%s,%.2f,%03d,%s,%s,%s,%s,%s",playername,i,target,distance,radial,quality,weapon,airframe,time,date) -end -end -_savefile(filename,scores) -end -function RANGE:onbeforeLoad(From,Event,To) -if io and lfs then -return true -else -self:E(self.id..string.format("WARNING: io and/or lfs not desanitized. Cannot load player results.")) -return false -end -end -function RANGE:onafterLoad(From,Event,To) -local function _loadfile(filename) -local f=io.open(filename,"rb") -if f then -local data=f:read("*all") -f:close() -return data -else -self:E(self.id..string.format("WARNING: Could not load player results from file %s. File might not exist just yet.",tostring(filename))) -return nil -end -end -local path=lfs.writedir()..[[Logs\]] -local filename=path..string.format("RANGE-%s_BombingResults.csv",self.rangename) -local text=string.format("Loading player bomb results from file %s",filename) -self:I(self.id..text) -local data=_loadfile(filename) -if data then -local results=UTILS.Split(data,"\n") -table.remove(results,1) -self.bombPlayerResults={} -for _,_result in pairs(results)do -local resultdata=UTILS.Split(_result,",") -local result={} -local playername=resultdata[1] -result.player=playername -result.name=tostring(resultdata[3]) -result.distance=tonumber(resultdata[4]) -result.radial=tonumber(resultdata[5]) -result.quality=tostring(resultdata[6]) -result.weapon=tostring(resultdata[7]) -result.airframe=tostring(resultdata[8]) -result.time=UTILS.ClockToSeconds(resultdata[9]or"00:00:00") -result.date=resultdata[10]or"n/a" -self.bombPlayerResults[playername]=self.bombPlayerResults[playername]or{} -table.insert(self.bombPlayerResults[playername],result) -end -end -end -function RANGE._DelayedSmoke(_args) -trigger.action.smoke(_args.coord:GetVec3(),_args.color) -end -function RANGE:_DisplayMyStrafePitResults(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local _message=string.format("My Top %d Strafe Pit Results:\n",self.ndisplayresult) -local _results=self.strafePlayerResults[_playername] -if _results==nil then -_message=string.format("%s: No Score yet.",_playername) -else -local _sort=function(a,b)return a.hits>b.hits end -table.sort(_results,_sort) -local _bestMsg="" -local _count=1 -for _,_result in pairs(_results)do -_message=_message..string.format("\n[%d] Hits %d - %s - %s",_count,_result.hits,_result.zone.name,_result.text) -if _bestMsg==""then -_bestMsg=string.format("Hits %d - %s - %s",_result.hits,_result.zone.name,_result.text) -end -if _count==self.ndisplayresult then -break -end -_count=_count+1 -end -_message=_message.."\n\nBEST: ".._bestMsg -end -self:_DisplayMessageToGroup(_unit,_message,nil,true,true) -end -end -function RANGE:_DisplayStrafePitResults(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local _playerResults={} -local _message=string.format("Strafe Pit Results - Top %d Players:\n",self.ndisplayresult) -for _playerName,_results in pairs(self.strafePlayerResults)do -local _best=nil -for _,_result in pairs(_results)do -if _best==nil or _result.hits>_best.hits then -_best=_result -end -end -if _best~=nil then -local text=string.format("%s: Hits %i - %s - %s",_playerName,_best.hits,_best.zone.name,_best.text) -table.insert(_playerResults,{msg=text,hits=_best.hits}) -end -end -local _sort=function(a,b)return a.hits>b.hits end -table.sort(_playerResults,_sort) -for _i=1,math.min(#_playerResults,self.ndisplayresult)do -_message=_message..string.format("\n[%d] %s",_i,_playerResults[_i].msg) -end -if#_playerResults<1 then -_message=_message.."No player scored yet." -end -self:_DisplayMessageToGroup(_unit,_message,nil,true,true) -end -end -function RANGE:_DisplayMyBombingResults(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local _message=string.format("My Top %d Bombing Results:\n",self.ndisplayresult) -local _results=self.bombPlayerResults[_playername] -if _results==nil then -_message=_playername..": No Score yet." -else -local _sort=function(a,b)return a.distance180 then -heading=heading-180 -else -heading=heading+180 -end -local mycoord=coord:ToStringA2G(_unit,_settings) -_text=_text..string.format("\n- %s: heading %03d°\n%s",_strafepit.name,heading,mycoord) -end -self:_DisplayMessageToGroup(_unit,_text,nil,true,true) -end -end -function RANGE:_DisplayRangeWeather(_unitname) -self:F(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local text="" -local coord=unit:GetCoordinate() -if self.location then -local position=self.location -local T=position:GetTemperature() -local P=position:GetPressure() -local Wd,Ws=position:GetWind() -local Bn,Bd=UTILS.BeaufortScale(Ws) -local WD=string.format('%03d°',Wd) -local Ts=string.format("%d°C",T) -local hPa2inHg=0.0295299830714 -local hPa2mmHg=0.7500615613030 -local settings=_DATABASE:GetPlayerSettings(playername)or _SETTINGS -local tT=string.format("%d°C",T) -local tW=string.format("%.1f m/s",Ws) -local tP=string.format("%.1f mmHg",P*hPa2mmHg) -if settings:IsImperial()then -tW=string.format("%.1f knots",UTILS.MpsToKnots(Ws)) -tP=string.format("%.2f inHg",P*hPa2inHg) -end -text=text..string.format("Weather Report at %s:\n",self.rangename) -text=text..string.format("--------------------------------------------------\n") -text=text..string.format("Temperature %s\n",tT) -text=text..string.format("Wind from %s at %s (%s)\n",WD,tW,Bd) -text=text..string.format("QFE %.1f hPa = %s",P,tP) -else -text=string.format("No range location defined for range %s.",self.rangename) -end -self:_DisplayMessageToGroup(unit,text,nil,true,true) -self:T2(self.id..text) -else -self:T(self.id..string.format("ERROR! Could not find player unit in RangeInfo! Name = %s",_unitname)) -end -end -function RANGE:_CheckPlayers() -for playername,_playersettings in pairs(self.PlayerSettings)do -local playersettings=_playersettings -local unitname=playersettings.unitname -local unit=UNIT:FindByName(unitname) -if unit and unit:IsAlive()then -if unit:IsInZone(self.rangezone)then -if not playersettings.inzone then -playersettings.inzone=true -self:EnterRange(playersettings) -end -else -if playersettings.inzone==true then -playersettings.inzone=false -self:ExitRange(playersettings) -end -end -end -end -end -function RANGE:_CheckInZone(_unitName) -self:F2(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local function checkme(targetheading,_zone) -local zone=_zone -local unitheading=_unit:GetHeading() -local pitheading=targetheading-180 -local deltaheading=unitheading-pitheading -local towardspit=math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90 -if towardspit then -local vec3=_unit:GetVec3() -local vec2={x=vec3.x,y=vec3.z} -local landheight=land.getHeight(vec2) -local unitalt=vec3.y-landheight -if unitalt<=self.strafemaxalt then -local unitinzone=zone:IsVec2InZone(vec2) -return unitinzone -end -end -return false -end -local _unitID=_unit:GetID() -local _currentStrafeRun=self.strafeStatus[_unitID] -if _currentStrafeRun then -local zone=_currentStrafeRun.zone.polygon -local unitinzone=checkme(_currentStrafeRun.zone.heading,zone) -if unitinzone then -_currentStrafeRun.time=_currentStrafeRun.time+1 -else -_currentStrafeRun.time=_currentStrafeRun.time+1 -if _currentStrafeRun.time<=3 then -self.strafeStatus[_unitID]=nil -local _msg=string.format("%s left strafing zone %s too quickly. No Score.",_playername,_currentStrafeRun.zone.name) -self:_DisplayMessageToGroup(_unit,_msg,nil,true) -if self.rangecontrol then -self.rangecontrol:NewTransmission(RANGE.Sound.RCLeftStrafePitTooQuickly.filename,RANGE.Sound.RCLeftStrafePitTooQuickly.duration,self.soundpath) -end -else -local _ammo=self:_GetAmmo(_unitName) -local _result=self.strafeStatus[_unitID] -local _sound=nil -if _result.hits>=_result.zone.goodPass*2 then -_result.text="EXCELLENT PASS" -_sound=RANGE.Sound.RCExcellentPass -elseif _result.hits>=_result.zone.goodPass then -_result.text="GOOD PASS" -_sound=RANGE.Sound.RCGoodPass -elseif _result.hits>=_result.zone.goodPass/2 then -_result.text="INEFFECTIVE PASS" -_sound=RANGE.Sound.RCIneffectivePass -else -_result.text="POOR PASS" -_sound=RANGE.Sound.RCPoorPass -end -local shots=_result.ammo-_ammo -local accur=0 -if shots>0 then -accur=_result.hits/shots*100 -end -local _text=string.format("%s, hits on target %s: %d",self:_myname(_unitName),_result.zone.name,_result.hits) -if shots and accur then -_text=_text..string.format("\nTotal rounds fired %d. Accuracy %.1f %%.",shots,accur) -end -_text=_text..string.format("\n%s",_result.text) -self:_DisplayMessageToGroup(_unit,_text) -if self.rangecontrol then -self.rangecontrol:NewTransmission(RANGE.Sound.RCHitsOnTarget.filename,RANGE.Sound.RCHitsOnTarget.duration,self.soundpath) -self.rangecontrol:Number2Transmission(string.format("%d",_result.hits)) -if shots and accur then -self.rangecontrol:NewTransmission(RANGE.Sound.RCTotalRoundsFired.filename,RANGE.Sound.RCTotalRoundsFired.duration,self.soundpath,nil,0.2) -self.rangecontrol:Number2Transmission(string.format("%d",shots),nil,0.2) -self.rangecontrol:NewTransmission(RANGE.Sound.RCAccuracy.filename,RANGE.Sound.RCAccuracy.duration,self.soundpath,nil,0.2) -self.rangecontrol:Number2Transmission(string.format("%d",UTILS.Round(accur,0))) -self.rangecontrol:NewTransmission(RANGE.Sound.RCPercent.filename,RANGE.Sound.RCPercent.duration,self.soundpath) -end -self.rangecontrol:NewTransmission(_sound.filename,_sound.duration,self.soundpath,nil,0.5) -end -self.strafeStatus[_unitID]=nil -local _stats=self.strafePlayerResults[_playername]or{} -table.insert(_stats,_result) -self.strafePlayerResults[_playername]=_stats -end -end -else -for _,_targetZone in pairs(self.strafeTargets)do -local zone=_targetZone.polygon -local unitinzone=checkme(_targetZone.heading,zone) -if unitinzone then -local _ammo=self:_GetAmmo(_unitName) -self.strafeStatus[_unitID]={hits=0,zone=_targetZone,time=1,ammo=_ammo,pastfoulline=false} -local _msg=string.format("%s, rolling in on strafe pit %s.",self:_myname(_unitName),_targetZone.name) -if self.rangecontrol then -self.rangecontrol:NewTransmission(RANGE.Sound.RCRollingInOnStrafeTarget.filename,RANGE.Sound.RCRollingInOnStrafeTarget.duration,self.soundpath) -end -self:_DisplayMessageToGroup(_unit,_msg,10,true) -break -end -end -end -end -end -function RANGE:_AddF10Commands(_unitName) -self:F(_unitName) -local _unit,playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and playername then -local group=_unit:GetGroup() -local _gid=group:GetID() -if group and _gid then -if not self.MenuAddedTo[_gid]then -self.MenuAddedTo[_gid]=true -local _rangePath=nil -if RANGE.MenuF10Root then -_rangePath=missionCommands.addSubMenuForGroup(_gid,self.rangename,RANGE.MenuF10Root) -else -if RANGE.MenuF10[_gid]==nil then -RANGE.MenuF10[_gid]=missionCommands.addSubMenuForGroup(_gid,"On the Range") -end -_rangePath=missionCommands.addSubMenuForGroup(_gid,self.rangename,RANGE.MenuF10[_gid]) -end -local _statsPath=missionCommands.addSubMenuForGroup(_gid,"Statistics",_rangePath) -local _markPath=missionCommands.addSubMenuForGroup(_gid,"Mark Targets",_rangePath) -local _settingsPath=missionCommands.addSubMenuForGroup(_gid,"My Settings",_rangePath) -local _infoPath=missionCommands.addSubMenuForGroup(_gid,"Range Info",_rangePath) -local _mysmokePath=missionCommands.addSubMenuForGroup(_gid,"Smoke Color",_settingsPath) -local _myflarePath=missionCommands.addSubMenuForGroup(_gid,"Flare Color",_settingsPath) -missionCommands.addCommandForGroup(_gid,"Mark On Map",_markPath,self._MarkTargetsOnMap,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Illuminate Range",_markPath,self._IlluminateBombTargets,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Smoke Strafe Pits",_markPath,self._SmokeStrafeTargetBoxes,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Smoke Strafe Tgts",_markPath,self._SmokeStrafeTargets,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Smoke Bomb Tgts",_markPath,self._SmokeBombTargets,self,_unitName) -missionCommands.addCommandForGroup(_gid,"All Strafe Results",_statsPath,self._DisplayStrafePitResults,self,_unitName) -missionCommands.addCommandForGroup(_gid,"All Bombing Results",_statsPath,self._DisplayBombingResults,self,_unitName) -missionCommands.addCommandForGroup(_gid,"My Strafe Results",_statsPath,self._DisplayMyStrafePitResults,self,_unitName) -missionCommands.addCommandForGroup(_gid,"My Bomb Results",_statsPath,self._DisplayMyBombingResults,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Reset All Stats",_statsPath,self._ResetRangeStats,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Blue Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Blue) -missionCommands.addCommandForGroup(_gid,"Green Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Green) -missionCommands.addCommandForGroup(_gid,"Orange Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Orange) -missionCommands.addCommandForGroup(_gid,"Red Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Red) -missionCommands.addCommandForGroup(_gid,"White Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.White) -missionCommands.addCommandForGroup(_gid,"Green Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.Green) -missionCommands.addCommandForGroup(_gid,"Red Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.Red) -missionCommands.addCommandForGroup(_gid,"White Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.White) -missionCommands.addCommandForGroup(_gid,"Yellow Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.Yellow) -missionCommands.addCommandForGroup(_gid,"Smoke Delay On/Off",_settingsPath,self._SmokeBombDelayOnOff,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Smoke Impact On/Off",_settingsPath,self._SmokeBombImpactOnOff,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Flare Hits On/Off",_settingsPath,self._FlareDirectHitsOnOff,self,_unitName) -missionCommands.addCommandForGroup(_gid,"All Messages On/Off",_settingsPath,self._MessagesToPlayerOnOff,self,_unitName) -missionCommands.addCommandForGroup(_gid,"General Info",_infoPath,self._DisplayRangeInfo,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Weather Report",_infoPath,self._DisplayRangeWeather,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Bombing Targets",_infoPath,self._DisplayBombTargets,self,_unitName) -missionCommands.addCommandForGroup(_gid,"Strafe Pits",_infoPath,self._DisplayStrafePits,self,_unitName) -end -else -self:E(self.id.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName) -end -else -self:E(self.id.."Player unit does not exist in AddF10Menu() function. Unit name: ".._unitName) -end -end -function RANGE:_GetBombTargetCoordinate(target) -local coord=nil -if target.type==RANGE.TargetType.UNIT then -if not target.move then -coord=target.coordinate -else -if target.target and target.target:IsAlive()then -coord=target.target:GetCoordinate() -end -end -elseif target.type==RANGE.TargetType.STATIC then -coord=target.coordinate -elseif target.type==RANGE.TargetType.COORD then -coord=target.coordinate -else -self:E(self.id.."ERROR: Unknown target type.") -end -return coord -end -function RANGE:_GetAmmo(unitname) -self:F2(unitname) -local ammo=0 -local unit,playername=self:_GetPlayerUnitAndName(unitname) -if unit and playername then -local has_ammo=false -local ammotable=unit:GetAmmo() -self:T2({ammotable=ammotable}) -if ammotable~=nil then -local weapons=#ammotable -self:T2(self.id..string.format("Number of weapons %d.",weapons)) -for w=1,weapons do -local Nammo=ammotable[w]["count"] -local Tammo=ammotable[w]["desc"]["typeName"] -if string.match(Tammo,"shell")then -ammo=ammo+Nammo -local text=string.format("Player %s has %d rounds ammo of type %s",playername,Nammo,Tammo) -self:T(self.id..text) -else -local text=string.format("Player %s has %d ammo of type %s",playername,Nammo,Tammo) -self:T(self.id..text) -end -end -end -end -return ammo -end -function RANGE:_MarkTargetsOnMap(_unitName) -self:F(_unitName) -local group=nil -if _unitName then -group=UNIT:FindByName(_unitName):GetGroup() -end -for _,_bombtarget in pairs(self.bombingTargets)do -local bombtarget=_bombtarget -local coord=self:_GetBombTargetCoordinate(_bombtarget) -if group then -coord:MarkToGroup(string.format("Bomb target %s:\n%s\n%s",bombtarget.name,coord:ToStringLLDMS(),coord:ToStringBULLS(group:GetCoalition())),group) -else -coord:MarkToAll(string.format("Bomb target %s",bombtarget.name)) -end -end -for _,_strafepit in pairs(self.strafeTargets)do -for _,_target in pairs(_strafepit.targets)do -local _target=_target -if _target and _target:IsAlive()then -local coord=_target:GetCoordinate() -if group then -coord:MarkToGroup(string.format("Strafe target %s:\n%s\n%s",_target:GetName(),coord:ToStringLLDMS(),coord:ToStringBULLS(group:GetCoalition())),group) -else -coord:MarkToAll("Strafe target ".._target:GetName()) -end -end -end -end -if _unitName then -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -local text=string.format("%s, %s, range targets are now marked on F10 map.",self.rangename,_playername) -self:_DisplayMessageToGroup(_unit,text,5) -end -end -function RANGE:_IlluminateBombTargets(_unitName) -self:F(_unitName) -local bomb={} -for _,_bombtarget in pairs(self.bombingTargets)do -local _target=_bombtarget.target -local coord=self:_GetBombTargetCoordinate(_bombtarget) -if coord then -table.insert(bomb,coord) -end -end -if#bomb>0 then -local coord=bomb[math.random(#bomb)] -local c=COORDINATE:New(coord.x,coord.y+math.random(self.illuminationminalt,self.illuminationmaxalt),coord.z) -c:IlluminationBomb() -end -local strafe={} -for _,_strafepit in pairs(self.strafeTargets)do -for _,_target in pairs(_strafepit.targets)do -local _target=_target -if _target and _target:IsAlive()then -local coord=_target:GetCoordinate() -table.insert(strafe,coord) -end -end -end -if#strafe>0 then -local coord=strafe[math.random(#strafe)] -local c=COORDINATE:New(coord.x,coord.y+math.random(self.illuminationminalt,self.illuminationmaxalt),coord.z) -c:IlluminationBomb() -end -if _unitName then -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -local text=string.format("%s, %s, range targets are illuminated.",self.rangename,_playername) -self:_DisplayMessageToGroup(_unit,text,5) -end -end -function RANGE:_ResetRangeStats(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -self.strafePlayerResults[_playername]=nil -self.bombPlayerResults[_playername]=nil -local text=string.format("%s, %s, your range stats were cleared.",self.rangename,_playername) -self:DisplayMessageToGroup(_unit,text,5,false,true) -end -end -function RANGE:_DisplayMessageToGroup(_unit,_text,_time,_clear,display) -self:F({unit=_unit,text=_text,time=_time,clear=_clear}) -_time=_time or self.Tmsg -if _clear==nil or _clear==false then -_clear=false -else -_clear=true -end -if self.messages==false then -return -end -if _unit and _unit:IsAlive()then -local _gid=_unit:GetGroup():GetID() -local _,playername=self:_GetPlayerUnitAndName(_unit:GetName()) -local playermessage=self.PlayerSettings[playername].messages -if _gid and(playermessage==true or display)and(not self.examinerexclusive)then -trigger.action.outTextForGroup(_gid,_text,_time,_clear) -end -if self.examinergroupname~=nil then -local _examinerid=GROUP:FindByName(self.examinergroupname):GetID() -if _examinerid then -trigger.action.outTextForGroup(_examinerid,_text,_time,_clear) -end -end -end -end -function RANGE:_SmokeBombImpactOnOff(unitname) -self:F(unitname) -local unit,playername=self:_GetPlayerUnitAndName(unitname) -if unit and playername then -local text -if self.PlayerSettings[playername].smokebombimpact==true then -self.PlayerSettings[playername].smokebombimpact=false -text=string.format("%s, %s, smoking impact points of bombs is now OFF.",self.rangename,playername) -else -self.PlayerSettigs[playername].smokebombimpact=true -text=string.format("%s, %s, smoking impact points of bombs is now ON.",self.rangename,playername) -end -self:_DisplayMessageToGroup(unit,text,5,false,true) -end -end -function RANGE:_SmokeBombDelayOnOff(unitname) -self:F(unitname) -local unit,playername=self:_GetPlayerUnitAndName(unitname) -if unit and playername then -local text -if self.PlayerSettings[playername].delaysmoke==true then -self.PlayerSettings[playername].delaysmoke=false -text=string.format("%s, %s, delayed smoke of bombs is now OFF.",self.rangename,playername) -else -self.PlayerSettigs[playername].delaysmoke=true -text=string.format("%s, %s, delayed smoke of bombs is now ON.",self.rangename,playername) -end -self:_DisplayMessageToGroup(unit,text,5,false,true) -end -end -function RANGE:_MessagesToPlayerOnOff(unitname) -self:F(unitname) -local unit,playername=self:_GetPlayerUnitAndName(unitname) -if unit and playername then -local text -if self.PlayerSettings[playername].messages==true then -text=string.format("%s, %s, display of ALL messages is now OFF.",self.rangename,playername) -else -text=string.format("%s, %s, display of ALL messages is now ON.",self.rangename,playername) -end -self:_DisplayMessageToGroup(unit,text,5,false,true) -self.PlayerSettings[playername].messages=not self.PlayerSettings[playername].messages -end -end -function RANGE:_FlareDirectHitsOnOff(unitname) -self:F(unitname) -local unit,playername=self:_GetPlayerUnitAndName(unitname) -if unit and playername then -local text -if self.PlayerSettings[playername].flaredirecthits==true then -self.PlayerSettings[playername].flaredirecthits=false -text=string.format("%s, %s, flaring direct hits is now OFF.",self.rangename,playername) -else -self.PlayerSettings[playername].flaredirecthits=true -text=string.format("%s, %s, flaring direct hits is now ON.",self.rangename,playername) -end -self:_DisplayMessageToGroup(unit,text,5,false,true) -end -end -function RANGE:_SmokeBombTargets(unitname) -self:F(unitname) -for _,_bombtarget in pairs(self.bombingTargets)do -local _target=_bombtarget.target -local coord=self:_GetBombTargetCoordinate(_bombtarget) -if coord then -coord:Smoke(self.BombSmokeColor) -end -end -if unitname then -local unit,playername=self:_GetPlayerUnitAndName(unitname) -local text=string.format("%s, %s, bombing targets are now marked with %s smoke.",self.rangename,playername,self:_smokecolor2text(self.BombSmokeColor)) -self:_DisplayMessageToGroup(unit,text,5) -end -end -function RANGE:_SmokeStrafeTargets(unitname) -self:F(unitname) -for _,_target in pairs(self.strafeTargets)do -_target.coordinate:Smoke(self.StrafeSmokeColor) -end -if unitname then -local unit,playername=self:_GetPlayerUnitAndName(unitname) -local text=string.format("%s, %s, strafing tragets are now marked with %s smoke.",self.rangename,playername,self:_smokecolor2text(self.StrafeSmokeColor)) -self:_DisplayMessageToGroup(unit,text,5) -end -end -function RANGE:_SmokeStrafeTargetBoxes(unitname) -self:F(unitname) -for _,_target in pairs(self.strafeTargets)do -local zone=_target.polygon -zone:SmokeZone(self.StrafePitSmokeColor,4) -for _,_point in pairs(_target.smokepoints)do -_point:SmokeOrange() -end -end -if unitname then -local unit,playername=self:_GetPlayerUnitAndName(unitname) -local text=string.format("%s, %s, strafing pit approach boxes are now marked with %s smoke.",self.rangename,playername,self:_smokecolor2text(self.StrafePitSmokeColor)) -self:_DisplayMessageToGroup(unit,text,5) -end -end -function RANGE:_playersmokecolor(_unitName,color) -self:F({unitname=_unitName,color=color}) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -self.PlayerSettings[_playername].smokecolor=color -local text=string.format("%s, %s, your bomb impacts are now smoked in %s.",self.rangename,_playername,self:_smokecolor2text(color)) -self:_DisplayMessageToGroup(_unit,text,5) -end -end -function RANGE:_playerflarecolor(_unitName,color) -self:F({unitname=_unitName,color=color}) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -self.PlayerSettings[_playername].flarecolor=color -local text=string.format("%s, %s, your direct hits are now flared in %s.",self.rangename,_playername,self:_flarecolor2text(color)) -self:_DisplayMessageToGroup(_unit,text,5) -end -end -function RANGE:_smokecolor2text(color) -self:F(color) -local txt="" -if color==SMOKECOLOR.Blue then -txt="blue" -elseif color==SMOKECOLOR.Green then -txt="green" -elseif color==SMOKECOLOR.Orange then -txt="orange" -elseif color==SMOKECOLOR.Red then -txt="red" -elseif color==SMOKECOLOR.White then -txt="white" -else -txt=string.format("unknown color (%s)",tostring(color)) -end -return txt -end -function RANGE:_flarecolor2text(color) -self:F(color) -local txt="" -if color==FLARECOLOR.Green then -txt="green" -elseif color==FLARECOLOR.Red then -txt="red" -elseif color==FLARECOLOR.White then -txt="white" -elseif color==FLARECOLOR.Yellow then -txt="yellow" -else -txt=string.format("unknown color (%s)",tostring(color)) -end -return txt -end -function RANGE:_CheckStatic(name) -self:F2(name) -local _DCSstatic=StaticObject.getByName(name) -if _DCSstatic and _DCSstatic:isExist()then -local _MOOSEstatic=STATIC:FindByName(name,false) -if not _MOOSEstatic then -self:T(self.id..string.format("Adding DCS static to MOOSE database. Name = %s.",name)) -_DATABASE:AddStatic(name) -end -return true -else -self:T3(self.id..string.format("No static object with name %s exists.",name)) -end -if UNIT:FindByName(name)then -return false -else -self:T3(self.id..string.format("No unit object with name %s exists.",name)) -end -return nil -end -function RANGE:_GetSpeed(controllable) -self:F2(controllable) -local desc=controllable:GetDesc() -local speed=0 -if desc then -speed=desc.speedMax*3.6 -self:T({speed=speed}) -end -return speed -end -function RANGE:_GetPlayerUnitAndName(_unitName) -self:F2(_unitName) -if _unitName~=nil then -local DCSunit=Unit.getByName(_unitName) -if DCSunit then -local playername=DCSunit:getPlayerName() -local unit=UNIT:Find(DCSunit) -self:T2({DCSunit=DCSunit,unit=unit,playername=playername}) -if DCSunit and unit and playername then -return unit,playername -end -end -end -return nil,nil -end -function RANGE:_myname(unitname) -self:F2(unitname) -local unit=UNIT:FindByName(unitname) -local pname=unit:GetPlayerName() -local csign=unit:GetCallsign() -return string.format("%s",pname) -end -do -ZONE_GOAL={ -ClassName="ZONE_GOAL", -Goal=nil, -SmokeTime=nil, -SmokeScheduler=nil, -SmokeColor=nil, -SmokeZone=nil, -} -function ZONE_GOAL:New(Zone) -local self=BASE:Inherit(self,ZONE_RADIUS:New(Zone:GetName(),Zone:GetVec2(),Zone:GetRadius())) -self:F({Zone=Zone}) -self.Goal=GOAL:New() -self.SmokeTime=nil -self:SetSmokeZone(true) -self:AddTransition("*","DestroyedUnit","*") -return self -end -function ZONE_GOAL:GetZone() -return self -end -function ZONE_GOAL:GetZoneName() -return self:GetName() -end -function ZONE_GOAL:SetSmokeZone(switch) -self.SmokeZone=switch -return self -end -function ZONE_GOAL:Smoke(SmokeColor) -self:F({SmokeColor=SmokeColor}) -self.SmokeColor=SmokeColor -end -function ZONE_GOAL:Flare(FlareColor) -self:FlareZone(FlareColor,30) -end -function ZONE_GOAL:onafterGuard() -self:F("Guard") -if self.SmokeZone and not self.SmokeScheduler then -self.SmokeScheduler=self:ScheduleRepeat(1,1,0.1,nil,self.StatusSmoke,self) -end -end -function ZONE_GOAL:StatusSmoke() -self:F({self.SmokeTime,self.SmokeColor}) -if self.SmokeZone then -local CurrentTime=timer.getTime() -if self.SmokeTime==nil or self.SmokeTime+300<=CurrentTime then -if self.SmokeColor then -self:GetCoordinate():Smoke(self.SmokeColor) -self.SmokeTime=CurrentTime -end -end -end -end -function ZONE_GOAL:__Destroyed(EventData) -self:F({"EventDead",EventData}) -self:F({EventData.IniUnit}) -if EventData.IniDCSUnit then -local Vec3=EventData.IniDCSUnit:getPosition().p -self:F({Vec3=Vec3}) -if Vec3 and self:IsVec3InZone(Vec3)then -local PlayerHits=_DATABASE.HITS[EventData.IniUnitName] -if PlayerHits then -for PlayerName,PlayerHit in pairs(PlayerHits.Players or{})do -self.Goal:AddPlayerContribution(PlayerName) -self:DestroyedUnit(EventData.IniUnitName,PlayerName) -end -end -end -end -end -function ZONE_GOAL:MonitorDestroyedUnits() -self:HandleEvent(EVENTS.Dead,self.__Destroyed) -self:HandleEvent(EVENTS.Crash,self.__Destroyed) -end -end -do -ZONE_GOAL_COALITION={ -ClassName="ZONE_GOAL_COALITION", -Coalition=nil, -PreviousCoaliton=nil, -UnitCategories=nil, -ObjectCategories=nil, -} -ZONE_GOAL_COALITION.States={} -function ZONE_GOAL_COALITION:New(Zone,Coalition,UnitCategories) -if not Zone then -BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITON!") -return nil -end -local self=BASE:Inherit(self,ZONE_GOAL:New(Zone)) -self:F({Zone=Zone,Coalition=Coalition}) -self:SetCoalition(Coalition or coalition.side.NEUTRAL) -self:SetUnitCategories(UnitCategories) -self:SetObjectCategories() -return self -end -function ZONE_GOAL_COALITION:SetCoalition(Coalition) -self.PreviousCoalition=self.Coalition or Coalition -self.Coalition=Coalition -return self -end -function ZONE_GOAL_COALITION:SetUnitCategories(UnitCategories) -if UnitCategories and type(UnitCategories)~="table"then -UnitCategories={UnitCategories} -end -self.UnitCategories=UnitCategories or{Unit.Category.GROUND_UNIT} -return self -end -function ZONE_GOAL_COALITION:SetObjectCategories(ObjectCategories) -if ObjectCategories and type(ObjectCategories)~="table"then -ObjectCategories={ObjectCategories} -end -self.ObjectCategories=ObjectCategories or{Object.Category.UNIT,Object.Category.STATIC} -return self -end -function ZONE_GOAL_COALITION:GetCoalition() -return self.Coalition -end -function ZONE_GOAL_COALITION:GetPreviousCoalition() -return self.PreviousCoalition -end -function ZONE_GOAL_COALITION:GetCoalitionName() -return UTILS.GetCoalitionName(self.Coalition) -end -function ZONE_GOAL_COALITION:StatusZone() -local State=self:GetState() -local text=string.format("Zone state=%s, Owner=%s, Scanning...",State,self:GetCoalitionName()) -self:F(text) -self:Scan(self.ObjectCategories,self.UnitCategories) -return self -end -end -do -ZONE_CAPTURE_COALITION={ -ClassName="ZONE_CAPTURE_COALITION", -MarkBlue=nil, -MarkRed=nil, -StartInterval=nil, -RepeatInterval=nil, -HitsOn=nil, -HitTimeLast=nil, -HitTimeAttackOver=nil, -MarkOn=nil, -} -function ZONE_CAPTURE_COALITION:New(Zone,Coalition,UnitCategories,ObjectCategories) -local self=BASE:Inherit(self,ZONE_GOAL_COALITION:New(Zone,Coalition,UnitCategories)) -self:F({Zone=Zone,Coalition=Coalition,UnitCategories=UnitCategories,ObjectCategories=ObjectCategories}) -self:SetObjectCategories(ObjectCategories) -self:SetSmokeZone(false) -self:SetMarkZone(true) -self:SetStartState("Empty") -do -end -do -end -do -end -do -end -self:AddTransition("*","Guard","Guarded") -self:AddTransition("*","Empty","Empty") -self:AddTransition({"Guarded","Empty"},"Attack","Attacked") -self:AddTransition({"Guarded","Attacked","Empty"},"Capture","Captured") -_EVENTDISPATCHER:CreateEventNewZoneGoal(self) -return self -end -function ZONE_CAPTURE_COALITION:Start(StartInterval,RepeatInterval) -self.StartInterval=StartInterval or 1 -self.RepeatInterval=RepeatInterval or 15 -if self.ScheduleStatusZone then -self:ScheduleStop(self.ScheduleStatusZone) -end -self.ScheduleStatusZone=self:ScheduleRepeat(self.StartInterval,self.RepeatInterval,0.1,nil,self.StatusZone,self) -self:HandleEvent(EVENTS.Hit,self.OnEventHit) -self:Mark() -return self -end -function ZONE_CAPTURE_COALITION:Stop() -if self.ScheduleStatusZone then -self:ScheduleStop(self.ScheduleStatusZone) -end -if self.SmokeScheduler then -self:ScheduleStop(self.SmokeScheduler) -end -self:UnHandleEvent(EVENTS.Hit) -end -function ZONE_CAPTURE_COALITION:SetMonitorHits(Switch,TimeAttackOver) -self.HitsOn=Switch -self.HitTimeAttackOver=TimeAttackOver or 5*60 -return self -end -function ZONE_CAPTURE_COALITION:SetMarkZone(Switch) -if Switch==nil or Switch==true then -self.MarkOn=true -else -self.MarkOn=false -end -return self -end -function ZONE_CAPTURE_COALITION:OnEventHit(EventData) -if self.HitsOn then -local UnitHit=EventData.TgtUnit -if UnitHit and UnitHit:IsInZone(self)and UnitHit:GetCoalition()==self.Coalition then -self.HitTimeLast=timer.getTime() -if self:GetState()~="Attacked"then -self:F2("Hit ==> Attack") -self:Attack() -end -end -end -end -function ZONE_CAPTURE_COALITION:onafterGuard() -self:F2("After Guard") -if self.SmokeZone and not self.SmokeScheduler then -self.SmokeScheduler=self:ScheduleRepeat(self.StartInterval,self.RepeatInterval,0.1,nil,self.StatusSmoke,self) -end -end -function ZONE_CAPTURE_COALITION:onenterGuarded() -self:F2("Enter Guarded") -self:Mark() -end -function ZONE_CAPTURE_COALITION:onenterCaptured() -self:F2("Enter Captured") -local NewCoalition=self:GetScannedCoalition() -self:F({NewCoalition=NewCoalition}) -self:SetCoalition(NewCoalition) -self:Mark() -self.Goal:Achieved() -end -function ZONE_CAPTURE_COALITION:onenterEmpty() -self:F2("Enter Empty") -self:Mark() -end -function ZONE_CAPTURE_COALITION:onenterAttacked() -self:F2("Enter Attacked") -self:Mark() -end -function ZONE_CAPTURE_COALITION:IsEmpty() -local IsEmpty=self:IsNoneInZone() -self:F({IsEmpty=IsEmpty}) -return IsEmpty -end -function ZONE_CAPTURE_COALITION:IsGuarded() -local IsGuarded=self:IsAllInZoneOfCoalition(self.Coalition) -self:F({IsGuarded=IsGuarded}) -return IsGuarded -end -function ZONE_CAPTURE_COALITION:IsCaptured() -local IsCaptured=self:IsAllInZoneOfOtherCoalition(self.Coalition) -self:F({IsCaptured=IsCaptured}) -return IsCaptured -end -function ZONE_CAPTURE_COALITION:IsAttacked() -local IsAttacked=self:IsSomeInZoneOfCoalition(self.Coalition) -self:F({IsAttacked=IsAttacked}) -return IsAttacked -end -function ZONE_CAPTURE_COALITION:StatusZone() -local State=self:GetState() -self:GetParent(self,ZONE_CAPTURE_COALITION).StatusZone(self) -local Tnow=timer.getTime() -if State~="Guarded"and self:IsGuarded()then -if self.HitTimeLast==nil or Tnow>=self.HitTimeLast+self.HitTimeAttackOver then -self:Guard() -self.HitTimeLast=nil -end -end -if State~="Empty"and self:IsEmpty()then -self:Empty() -end -if State~="Attacked"and self:IsAttacked()then -self:Attack() -end -if State~="Captured"and self:IsCaptured()then -self:Capture() -end -local unitset=self:GetScannedSetUnit() -local nRed=0 -local nBlue=0 -for _,object in pairs(unitset:GetSet())do -local coal=object:GetCoalition() -if object:IsAlive()then -if coal==coalition.side.RED then -nRed=nRed+1 -elseif coal==coalition.side.BLUE then -nBlue=nBlue+1 -end -end -end -if false then -local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s",self:GetZoneName(),self:GetCoalitionName(),UTILS.GetCoalitionName(self:GetPreviousCoalition()),nBlue,nRed,State) -local NewState=self:GetState() -if NewState~=State then -text=text..string.format(" --> %s",NewState) -end -self:I(text) -end -end -function ZONE_CAPTURE_COALITION:Mark() -if self.MarkOn then -local Coord=self:GetCoordinate() -local ZoneName=self:GetZoneName() -local State=self:GetState() -if self.MarkRed then -Coord:RemoveMark(self.MarkRed) -end -if self.MarkBlue then -Coord:RemoveMark(self.MarkBlue) -end -if self.Coalition==coalition.side.BLUE then -self.MarkBlue=Coord:MarkToCoalitionBlue("Coalition: Blue\nGuard Zone: "..ZoneName.."\nStatus: "..State) -self.MarkRed=Coord:MarkToCoalitionRed("Coalition: Blue\nCapture Zone: "..ZoneName.."\nStatus: "..State) -elseif self.Coalition==coalition.side.RED then -self.MarkRed=Coord:MarkToCoalitionRed("Coalition: Red\nGuard Zone: "..ZoneName.."\nStatus: "..State) -self.MarkBlue=Coord:MarkToCoalitionBlue("Coalition: Red\nCapture Zone: "..ZoneName.."\nStatus: "..State) -else -self.MarkRed=Coord:MarkToCoalitionRed("Coalition: Neutral\nCapture Zone: "..ZoneName.."\nStatus: "..State) -self.MarkBlue=Coord:MarkToCoalitionBlue("Coalition: Neutral\nCapture Zone: "..ZoneName.."\nStatus: "..State) -end -end -end -end -ARTY={ -ClassName="ARTY", -lid=nil, -Debug=false, -targets={}, -moves={}, -currentTarget=nil, -currentMove=nil, -Nammo0=0, -Nshells0=0, -Nrockets0=0, -Nmissiles0=0, -Nukes0=0, -Nillu0=0, -Nsmoke0=0, -StatusInterval=10, -WaitForShotTime=300, -DCSdesc=nil, -Type=nil, -DisplayName=nil, -groupname=nil, -alias=nil, -clusters={}, -ismobile=true, -iscargo=false, -cargogroup=nil, -IniGroupStrength=0, -IsArtillery=nil, -RearmingDistance=100, -RearmingGroup=nil, -RearmingGroupSpeed=nil, -RearmingGroupOnRoad=false, -RearmingGroupCoord=nil, -RearmingPlaceCoord=nil, -RearmingArtyOnRoad=false, -InitialCoord=nil, -report=true, -ammoshells={}, -ammorockets={}, -ammomissiles={}, -Nshots=0, -minrange=300, -maxrange=1000000, -nukewarhead=75000, -Nukes=nil, -nukefire=false, -nukefires=nil, -nukerange=nil, -Nillu=nil, -illuPower=1000000, -illuMinalt=500, -illuMaxalt=1000, -Nsmoke=nil, -smokeColor=SMOKECOLOR.Red, -relocateafterfire=false, -relocateRmin=300, -relocateRmax=800, -markallow=false, -markkey=nil, -markreadonly=false, -autorelocate=false, -autorelocatemaxdist=50000, -autorelocateonroad=false, -coalition=nil, -respawnafterdeath=false, -respawndelay=nil -} -ARTY.WeaponType={ -Auto=1073741822, -Cannon=805306368, -Rockets=30720, -CruiseMissile=2097152, -TacticalNukes=666, -IlluminationShells=667, -SmokeShells=668, -} -ARTY.db={ -["2B11 mortar"]={ -minrange=500, -maxrange=7000, -reloadtime=30, -}, -["SPH 2S1 Gvozdika"]={ -minrange=300, -maxrange=15000, -reloadtime=nil, -}, -["SPH 2S19 Msta"]={ -minrange=300, -maxrange=23500, -reloadtime=nil, -}, -["SPH 2S3 Akatsia"]={ -minrange=300, -maxrange=17000, -reloadtime=nil, -}, -["SPH 2S9 Nona"]={ -minrange=500, -maxrange=7000, -reloadtime=nil, -}, -["SPH M109 Paladin"]={ -minrange=300, -maxrange=22000, -reloadtime=nil, -}, -["SpGH Dana"]={ -minrange=300, -maxrange=18700, -reloadtime=nil, -}, -["MLRS BM-21 Grad"]={ -minrange=5000, -maxrange=19000, -reloadtime=420, -}, -["MLRS 9K57 Uragan BM-27"]={ -minrange=11500, -maxrange=35800, -reloadtime=840, -}, -["MLRS 9A52 Smerch"]={ -minrange=20000, -maxrange=70000, -reloadtime=2160, -}, -["MLRS M270"]={ -minrange=10000, -maxrange=32000, -reloadtime=540, -}, -} -ARTY.version="1.2.0" -function ARTY:New(group,alias) -local self=BASE:Inherit(self,FSM_CONTROLLABLE:New()) -if type(group)=="string"then -self.groupname=group -group=GROUP:FindByName(group) -if not group then -self:E(string.format("ERROR: Requested ARTY group %s does not exist! (Has to be a MOOSE group.)",self.groupname)) -return nil -end -end -if group then -self:T(string.format("ARTY script version %s. Added group %s.",ARTY.version,group:GetName())) -else -self:E("ERROR: Requested ARTY group does not exist! (Has to be a MOOSE group.)") -return nil -end -if not(group:IsGround()or group:IsShip())then -self:E(string.format("ERROR: ARTY group %s has to be a GROUND or SHIP group!",group:GetName())) -return nil -end -self:SetControllable(group) -self.groupname=group:GetName() -self.coalition=group:GetCoalition() -if alias~=nil then -self.alias=tostring(alias) -else -self.alias=self.groupname -end -self.lid=string.format("ARTY %s | ",self.alias) -self.InitialCoord=group:GetCoordinate() -local DCSgroup=Group.getByName(group:GetName()) -local DCSunit=DCSgroup:getUnit(1) -self.DCSdesc=DCSunit:getDesc() -self:T3(self.lid.."DCS descriptors for group "..group:GetName()) -for id,desc in pairs(self.DCSdesc)do -self:T3({id=id,desc=desc}) -end -self.SpeedMax=group:GetSpeedMax() -if self.SpeedMax>1 then -self.ismobile=true -else -self.ismobile=false -end -self.Speed=self.SpeedMax*0.7 -self.DisplayName=self.DCSdesc.displayName -self.IsArtillery=DCSunit:hasAttribute("Artillery") -self.Type=group:GetTypeName() -self.IniGroupStrength=#group:GetUnits() -self:AddTransition("*","Start","CombatReady") -self:AddTransition("CombatReady","OpenFire","Firing") -self:AddTransition("Firing","CeaseFire","CombatReady") -self:AddTransition("CombatReady","Winchester","OutOfAmmo") -self:AddTransition({"CombatReady","OutOfAmmo"},"Rearm","Rearming") -self:AddTransition("Rearming","Rearmed","Rearmed") -self:AddTransition("*","Move","Moving") -self:AddTransition("Moving","Arrived","Arrived") -self:AddTransition("*","NewTarget","*") -self:AddTransition("*","CombatReady","CombatReady") -self:AddTransition("*","Status","*") -self:AddTransition("*","NewMove","*") -self:AddTransition("*","Dead","*") -self:AddTransition("*","Respawn","CombatReady") -self:AddTransition("*","Loaded","InTransit") -self:AddTransition("InTransit","UnLoaded","CombatReady") -self:AddTransition("Rearming","Arrived","Rearming") -self:AddTransition("Rearming","Move","Rearming") -return self -end -function ARTY:NewFromCargoGroup(cargogroup,alias) -if cargogroup then -BASE:T(string.format("ARTY script version %s. Added CARGO group %s.",ARTY.version,cargogroup:GetName())) -else -BASE:E("ERROR: Requested ARTY CARGO GROUP does not exist! (Has to be a MOOSE CARGO(!) group.)") -return nil -end -local group=cargogroup:GetObject() -local arty=ARTY:New(group,alias) -arty.iscargo=true -arty.cargogroup=cargogroup -return arty -end -function ARTY:AssignTargetCoord(coord,prio,radius,nshells,maxengage,time,weapontype,name,unique) -self:F({coord=coord,prio=prio,radius=radius,nshells=nshells,maxengage=maxengage,time=time,weapontype=weapontype,name=name,unique=unique}) -nshells=nshells or 5 -radius=radius or 100 -maxengage=maxengage or 1 -prio=prio or 50 -prio=math.max(1,prio) -prio=math.min(100,prio) -if unique==nil then -unique=false -end -weapontype=weapontype or ARTY.WeaponType.Auto -local text=nil -if coord:IsInstanceOf("GROUP")then -text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a GROUP. Converting to COORDINATE..." -coord=coord:GetCoordinate() -elseif coord:IsInstanceOf("UNIT")then -text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a UNIT. Converting to COORDINATE..." -coord=coord:GetCoordinate() -elseif coord:IsInstanceOf("POSITIONABLE")then -text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a POSITIONABLE. Converting to COORDINATE..." -coord=coord:GetCoordinate() -elseif coord:IsInstanceOf("COORDINATE")then -else -text="ERROR: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter!" -MESSAGE:New(text,30):ToAll() -self:E(self.lid..text) -return nil -end -if text~=nil then -self:E(self.lid..text) -end -local _name=name or coord:ToStringLLDMS() -local _unique=true -_name,_unique=self:_CheckName(self.targets,_name,not unique) -if unique==true and _unique==false then -self:T(self.lid..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!",self.groupname,_name)) -return nil -end -local _time -if type(time)=="string"then -_time=self:_ClockToSeconds(time) -elseif type(time)=="number"then -_time=timer.getAbsTime()+time -else -_time=timer.getAbsTime() -end -local _target={name=_name,coord=coord,radius=radius,nshells=nshells,engaged=0,underfire=false,prio=prio,maxengage=maxengage,time=_time,weapontype=weapontype} -table.insert(self.targets,_target) -self:__NewTarget(1,_target) -return _name -end -function ARTY:AssignAttackGroup(group,prio,radius,nshells,maxengage,time,weapontype,name,unique) -nshells=nshells or 5 -radius=radius or 100 -maxengage=maxengage or 1 -prio=prio or 50 -prio=math.max(1,prio) -prio=math.min(100,prio) -if unique==nil then -unique=false -end -weapontype=weapontype or ARTY.WeaponType.Auto -if type(group)=="string"then -group=GROUP:FindByName(group) -end -if group and group:IsAlive()then -local coord=group:GetCoordinate() -local _name=group:GetName() -local _unique=true -_name,_unique=self:_CheckName(self.targets,_name,not unique) -if unique==true and _unique==false then -self:T(self.lid..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!",self.groupname,_name)) -return nil -end -local _time -if type(time)=="string"then -_time=self:_ClockToSeconds(time) -elseif type(time)=="number"then -_time=timer.getAbsTime()+time -else -_time=timer.getAbsTime() -end -local target={} -target.attackgroup=true -target.name=_name -target.coord=coord -target.radius=radius -target.nshells=nshells -target.engaged=0 -target.underfire=false -target.prio=prio -target.time=_time -target.maxengage=maxengage -target.weapontype=weapontype -table.insert(self.targets,target) -self:__NewTarget(1,target) -return _name -else -self:E("ERROR: Group does not exist!") -end -return nil -end -function ARTY:AssignMoveCoord(coord,time,speed,onroad,cancel,name,unique) -self:F({coord=coord,time=time,speed=speed,onroad=onroad,cancel=cancel,name=name,unique=unique}) -if not self.ismobile then -self:T(self.lid..string.format("%s: group is immobile. Rejecting move request!",self.groupname)) -return nil -end -if unique==nil then -unique=false -end -local _name=name or coord:ToStringLLDMS() -local _unique=true -_name,_unique=self:_CheckName(self.moves,_name,not unique) -if unique==true and _unique==false then -self:T(self.lid..string.format("%s: move %s should have a unique name but name was already given. Rejecting move!",self.groupname,_name)) -return nil -end -if speed then -speed=math.min(speed,self.SpeedMax) -elseif self.Speed then -speed=self.Speed -else -speed=self.SpeedMax*0.7 -end -if onroad==nil then -onroad=false -end -if cancel==nil then -cancel=false -end -local _time -if type(time)=="string"then -_time=self:_ClockToSeconds(time) -elseif type(time)=="number"then -_time=timer.getAbsTime()+time -else -_time=timer.getAbsTime() -end -local _move={name=_name,coord=coord,time=_time,speed=speed,onroad=onroad,cancel=cancel} -table.insert(self.moves,_move) -return _name -end -function ARTY:SetAlias(alias) -self:F({alias=alias}) -self.alias=tostring(alias) -return self -end -function ARTY:AddToCluster(clusters) -self:F({clusters=clusters}) -local names -if type(clusters)=="table"then -names=clusters -elseif type(clusters)=="string"then -names={clusters} -else -self:E(self.lid.."ERROR: Input parameter must be a string or a table in ARTY:AddToCluster()!") -return -end -for _,cluster in pairs(names)do -table.insert(self.clusters,cluster) -end -return self -end -function ARTY:SetMinFiringRange(range) -self:F({range=range}) -self.minrange=range*1000 or 100 -return self -end -function ARTY:SetMaxFiringRange(range) -self:F({range=range}) -self.maxrange=range*1000 or 1000*1000 -return self -end -function ARTY:SetStatusInterval(interval) -self:F({interval=interval}) -self.StatusInterval=interval or 10 -return self -end -function ARTY:SetWaitForShotTime(waittime) -self:F({waittime=waittime}) -self.WaitForShotTime=waittime or 300 -return self -end -function ARTY:SetRearmingDistance(distance) -self:F({distance=distance}) -self.RearmingDistance=distance or 100 -return self -end -function ARTY:SetRearmingGroup(group) -self:F({group=group}) -self.RearmingGroup=group -return self -end -function ARTY:SetRearmingGroupSpeed(speed) -self:F({speed=speed}) -self.RearmingGroupSpeed=speed -return self -end -function ARTY:SetRearmingGroupOnRoad(onroad) -self:F({onroad=onroad}) -if onroad==nil then -onroad=true -end -self.RearmingGroupOnRoad=onroad -return self -end -function ARTY:SetRearmingArtyOnRoad(onroad) -self:F({onroad=onroad}) -if onroad==nil then -onroad=true -end -self.RearmingArtyOnRoad=onroad -return self -end -function ARTY:SetRearmingPlace(coord) -self:F({coord=coord}) -self.RearmingPlaceCoord=coord -return self -end -function ARTY:SetAutoRelocateToFiringRange(maxdistance,onroad) -self:F({distance=maxdistance,onroad=onroad}) -self.autorelocate=true -self.autorelocatemaxdist=maxdistance or 50 -self.autorelocatemaxdist=self.autorelocatemaxdist*1000 -if onroad==nil then -onroad=false -end -self.autorelocateonroad=onroad -return self -end -function ARTY:SetAutoRelocateAfterEngagement(rmax,rmin) -self.relocateafterfire=true -self.relocateRmax=rmax or 800 -self.relocateRmin=rmin or 300 -self.relocateRmin=math.min(self.relocateRmin,self.relocateRmax) -return self -end -function ARTY:SetReportON() -self.report=true -return self -end -function ARTY:SetReportOFF() -self.report=false -return self -end -function ARTY:SetRespawnOnDeath(delay) -self.respawnafterdeath=true -self.respawndelay=delay -return self -end -function ARTY:SetDebugON() -self.Debug=true -return self -end -function ARTY:SetDebugOFF() -self.Debug=false -return self -end -function ARTY:SetSpeed(speed) -self.Speed=speed -return self -end -function ARTY:RemoveTarget(name) -self:F2(name) -local id=self:_GetTargetIndexByName(name) -if id then -self:T(self.lid..string.format("Group %s: Removing target %s (id=%d).",self.groupname,name,id)) -table.remove(self.targets,id) -if self.markallow then -local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name) -if batteryname==self.groupname and markTargetID~=nil then -COORDINATE:RemoveMark(markTargetID) -end -end -end -self:T(self.lid..string.format("Group %s: Number of targets = %d.",self.groupname,#self.targets)) -end -function ARTY:RemoveMove(name) -self:F2(name) -local id=self:_GetMoveIndexByName(name) -if id then -self:T(self.lid..string.format("Group %s: Removing move %s (id=%d).",self.groupname,name,id)) -table.remove(self.moves,id) -if self.markallow then -local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name) -if batteryname==self.groupname and markMoveID~=nil then -COORDINATE:RemoveMark(markMoveID) -end -end -end -self:T(self.lid..string.format("Group %s: Number of moves = %d.",self.groupname,#self.moves)) -end -function ARTY:RemoveAllTargets() -self:F2() -for _,target in pairs(self.targets)do -self:RemoveTarget(target.name) -end -end -function ARTY:SetShellTypes(tableofnames) -self:F2(tableofnames) -self.ammoshells={} -for _,_type in pairs(tableofnames)do -table.insert(self.ammoshells,_type) -end -return self -end -function ARTY:SetRocketTypes(tableofnames) -self:F2(tableofnames) -self.ammorockets={} -for _,_type in pairs(tableofnames)do -table.insert(self.ammorockets,_type) -end -return self -end -function ARTY:SetMissileTypes(tableofnames) -self:F2(tableofnames) -self.ammomissiles={} -for _,_type in pairs(tableofnames)do -table.insert(self.ammomissiles,_type) -end -return self -end -function ARTY:SetTacNukeShells(n) -self.Nukes=n -return self -end -function ARTY:SetTacNukeWarhead(strength) -self.nukewarhead=strength or 0.075 -self.nukewarhead=self.nukewarhead*1000*1000 -return self -end -function ARTY:SetIlluminationShells(n,power) -self.Nillu=n -self.illuPower=power or 1.0 -self.illuPower=self.illuPower*1000000 -return self -end -function ARTY:SetIlluminationMinMaxAlt(minalt,maxalt) -self.illuMinalt=minalt or 500 -self.illuMaxalt=maxalt or 1000 -if self.illuMinalt>self.illuMaxalt then -self.illuMinalt=self.illuMaxalt -end -return self -end -function ARTY:SetSmokeShells(n,color) -self.Nsmoke=n -self.smokeColor=color or SMOKECOLOR.Red -return self -end -function ARTY:SetTacNukeFires(nfires,range) -self.nukefire=true -self.nukefires=nfires -self.nukerange=range -return self -end -function ARTY:SetMarkAssignmentsOn(key,readonly) -self.markkey=key -self.markallow=true -if readonly==nil then -self.markreadonly=false -end -return self -end -function ARTY:SetMarkTargetsOff() -self.markallow=false -self.markkey=nil -return self -end -function ARTY:onafterStart(Controllable,From,Event,To) -self:_EventFromTo("onafterStart",Event,From,To) -local text=string.format("Started ARTY version %s for group %s.",ARTY.version,Controllable:GetName()) -self:I(self.lid..text) -MESSAGE:New(text,5):ToAllIf(self.Debug) -self.Nammo0,self.Nshells0,self.Nrockets0,self.Nmissiles0=self:GetAmmo(self.Debug) -if self.nukerange==nil then -self.nukerange=1500/75000*self.nukewarhead -end -if self.nukefires==nil then -self.nukefires=20/1000/1000*self.nukerange*self.nukerange -end -if self.Nukes~=nil then -self.Nukes0=math.min(self.Nukes,self.Nshells0) -else -self.Nukes=0 -self.Nukes0=0 -end -if self.Nillu~=nil then -self.Nillu0=math.min(self.Nillu,self.Nshells0) -else -self.Nillu=0 -self.Nillu0=0 -end -if self.Nsmoke~=nil then -self.Nsmoke0=math.min(self.Nsmoke,self.Nshells0) -else -self.Nsmoke=0 -self.Nsmoke0=0 -end -local _dbproperties=self:_CheckDB(self.DisplayName) -self:T({dbproperties=_dbproperties}) -if _dbproperties~=nil then -for property,value in pairs(_dbproperties)do -self:T({property=property,value=value}) -self[property]=value -end -end -if not self.ismobile then -self.RearmingPlaceCoord=nil -self.relocateafterfire=false -self.autorelocate=false -end -self.Speed=math.min(self.Speed,self.SpeedMax) -if self.RearmingGroup then -local speedmax=self.RearmingGroup:GetSpeedMax() -self:T(self.lid..string.format("%s, rearming group %s max speed = %.1f km/h.",self.groupname,self.RearmingGroup:GetName(),speedmax)) -if self.RearmingGroupSpeed==nil then -self.RearmingGroupSpeed=speedmax*0.5 -else -self.RearmingGroupSpeed=math.min(self.RearmingGroupSpeed,self.RearmingGroup:GetSpeedMax()) -end -else -self.RearmingGroupSpeed=23 -end -local text=string.format("\n******************************************************\n") -text=text..string.format("Arty group = %s\n",self.groupname) -text=text..string.format("Arty alias = %s\n",self.alias) -text=text..string.format("Artillery attribute = %s\n",tostring(self.IsArtillery)) -text=text..string.format("Type = %s\n",self.Type) -text=text..string.format("Display Name = %s\n",self.DisplayName) -text=text..string.format("Number of units = %d\n",self.IniGroupStrength) -text=text..string.format("Speed max = %d km/h\n",self.SpeedMax) -text=text..string.format("Speed default = %d km/h\n",self.Speed) -text=text..string.format("Is mobile = %s\n",tostring(self.ismobile)) -text=text..string.format("Is cargo = %s\n",tostring(self.iscargo)) -text=text..string.format("Min range = %.1f km\n",self.minrange/1000) -text=text..string.format("Max range = %.1f km\n",self.maxrange/1000) -text=text..string.format("Total ammo count = %d\n",self.Nammo0) -text=text..string.format("Number of shells = %d\n",self.Nshells0) -text=text..string.format("Number of rockets = %d\n",self.Nrockets0) -text=text..string.format("Number of missiles = %d\n",self.Nmissiles0) -text=text..string.format("Number of nukes = %d\n",self.Nukes0) -text=text..string.format("Nuclear warhead = %d tons TNT\n",self.nukewarhead/1000) -text=text..string.format("Nuclear demolition = %d m\n",self.nukerange) -text=text..string.format("Nuclear fires = %d (active=%s)\n",self.nukefires,tostring(self.nukefire)) -text=text..string.format("Number of illum. = %d\n",self.Nillu0) -text=text..string.format("Illuminaton Power = %.3f mcd\n",self.illuPower/1000000) -text=text..string.format("Illuminaton Minalt = %d m\n",self.illuMinalt) -text=text..string.format("Illuminaton Maxalt = %d m\n",self.illuMaxalt) -text=text..string.format("Number of smoke = %d\n",self.Nsmoke0) -text=text..string.format("Smoke color = %d\n",self.smokeColor) -if self.RearmingGroup or self.RearmingPlaceCoord then -text=text..string.format("Rearming safe dist. = %d m\n",self.RearmingDistance) -end -if self.RearmingGroup then -text=text..string.format("Rearming group = %s\n",self.RearmingGroup:GetName()) -text=text..string.format("Rearming group speed= %d km/h\n",self.RearmingGroupSpeed) -text=text..string.format("Rearming group roads= %s\n",tostring(self.RearmingGroupOnRoad)) -end -if self.RearmingPlaceCoord then -local dist=self.InitialCoord:Get2DDistance(self.RearmingPlaceCoord) -text=text..string.format("Rearming coord dist = %d m\n",dist) -text=text..string.format("Rearming ARTY roads = %s\n",tostring(self.RearmingArtyOnRoad)) -end -text=text..string.format("Relocate after fire = %s\n",tostring(self.relocateafterfire)) -text=text..string.format("Relocate min dist. = %d m\n",self.relocateRmin) -text=text..string.format("Relocate max dist. = %d m\n",self.relocateRmax) -text=text..string.format("Auto move in range = %s\n",tostring(self.autorelocate)) -text=text..string.format("Auto move dist. max = %.1f km\n",self.autorelocatemaxdist/1000) -text=text..string.format("Auto move on road = %s\n",tostring(self.autorelocateonroad)) -text=text..string.format("Marker assignments = %s\n",tostring(self.markallow)) -text=text..string.format("Marker auth. key = %s\n",tostring(self.markkey)) -text=text..string.format("Marker readonly = %s\n",tostring(self.markreadonly)) -text=text..string.format("Clusters:\n") -for _,cluster in pairs(self.clusters)do -text=text..string.format("- %s\n",tostring(cluster)) -end -text=text..string.format("******************************************************\n") -text=text..string.format("Targets:\n") -for _,target in pairs(self.targets)do -text=text..string.format("- %s\n",self:_TargetInfo(target)) -local possible=self:_CheckWeaponTypePossible(target) -if not possible then -self:E(self.lid..string.format("WARNING: Selected weapon type %s is not possible",self:_WeaponTypeName(target.weapontype))) -end -if self.Debug then -local zone=ZONE_RADIUS:New(target.name,target.coord:GetVec2(),target.radius) -zone:BoundZone(180,coalition.side.NEUTRAL) -end -end -text=text..string.format("Moves:\n") -for i=1,#self.moves do -text=text..string.format("- %s\n",self:_MoveInfo(self.moves[i])) -end -text=text..string.format("******************************************************\n") -text=text..string.format("Shell types:\n") -for _,_type in pairs(self.ammoshells)do -text=text..string.format("- %s\n",_type) -end -text=text..string.format("Rocket types:\n") -for _,_type in pairs(self.ammorockets)do -text=text..string.format("- %s\n",_type) -end -text=text..string.format("Missile types:\n") -for _,_type in pairs(self.ammomissiles)do -text=text..string.format("- %s\n",_type) -end -text=text..string.format("******************************************************") -if self.Debug then -self:I(self.lid..text) -else -self:T(self.lid..text) -end -self.Controllable:OptionROEHoldFire() -self:HandleEvent(EVENTS.Shot) -self:HandleEvent(EVENTS.Dead) -if self.markallow then -world.addEventHandler(self) -end -self:__Status(self.StatusInterval) -end -function ARTY:_CheckDB(displayname) -for _type,_properties in pairs(ARTY.db)do -self:T({type=_type,properties=_properties}) -if _type==displayname then -self:T({type=_type,properties=_properties}) -return _properties -end -end -return nil -end -function ARTY:_StatusReport(display) -if display==nil then -display=false -end -local Nammo,Nshells,Nrockets,Nmissiles=self:GetAmmo() -local Nnukes=self.Nukes -local Nillu=self.Nillu -local Nsmoke=self.Nsmoke -local Tnow=timer.getTime() -local Clock=self:_SecondsToClock(timer.getAbsTime()) -local text=string.format("\n******************* STATUS ***************************\n") -text=text..string.format("ARTY group = %s\n",self.groupname) -text=text..string.format("Clock = %s\n",Clock) -text=text..string.format("FSM state = %s\n",self:GetState()) -text=text..string.format("Total ammo count = %d\n",Nammo) -text=text..string.format("Number of shells = %d\n",Nshells) -text=text..string.format("Number of rockets = %d\n",Nrockets) -text=text..string.format("Number of missiles = %d\n",Nmissiles) -text=text..string.format("Number of nukes = %d\n",Nnukes) -text=text..string.format("Number of illum. = %d\n",Nillu) -text=text..string.format("Number of smoke = %d\n",Nsmoke) -if self.currentTarget then -text=text..string.format("Current Target = %s\n",tostring(self.currentTarget.name)) -text=text..string.format("Curr. Tgt assigned = %d\n",Tnow-self.currentTarget.Tassigned) -else -text=text..string.format("Current Target = %s\n","none") -end -text=text..string.format("Nshots curr. Target = %d\n",self.Nshots) -text=text..string.format("Targets:\n") -for i=1,#self.targets do -text=text..string.format("- %s\n",self:_TargetInfo(self.targets[i])) -end -if self.currentMove then -text=text..string.format("Current Move = %s\n",tostring(self.currentMove.name)) -else -text=text..string.format("Current Move = %s\n","none") -end -text=text..string.format("Moves:\n") -for i=1,#self.moves do -text=text..string.format("- %s\n",self:_MoveInfo(self.moves[i])) -end -text=text..string.format("******************************************************") -env.info(self.lid..text) -MESSAGE:New(text,20):Clear():ToCoalitionIf(self.coalition,display) -end -function ARTY:OnEventShot(EventData) -self:F(EventData) -local _weapon=EventData.Weapon:getTypeName() -local _weaponStrArray=self:_split(_weapon,"%.") -local _weaponName=_weaponStrArray[#_weaponStrArray] -self:T3(self.lid.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) -self:T3(self.lid.."EVENT SHOT: Ini group = "..EventData.IniGroupName) -self:T3(self.lid.."EVENT SHOT: Weapon type = ".._weapon) -self:T3(self.lid.."EVENT SHOT: Weapon name = ".._weaponName) -local group=EventData.IniGroup -if group and group:IsAlive()then -if EventData.IniGroupName==self.groupname then -if self.currentTarget then -self.Nshots=self.Nshots+1 -local text=string.format("%s, fired shot %d of %d with weapon %s on target %s.",self.alias,self.Nshots,self.currentTarget.nshells,_weaponName,self.currentTarget.name) -self:T(self.lid..text) -MESSAGE:New(text,5):Clear():ToAllIf(self.report or self.Debug) -local _lastpos={x=0,y=0,z=0} -local function _TrackWeapon(_data) -local _weaponalive,_currpos=pcall( -function() -return _data.weapon:getPoint() -end) -self:T3(self.lid..string.format("ARTY %s: Weapon still in air: %s",self.groupname,tostring(_weaponalive))) -local _destroyweapon=false -if _weaponalive then -_lastpos={x=_currpos.x,y=_currpos.y,z=_currpos.z} -local _coord=COORDINATE:NewFromVec3(_lastpos) -local _dist=_coord:Get2DDistance(_data.target.coord) -self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m",self.groupname,_dist)) -if _data.target.weapontype==ARTY.WeaponType.IlluminationShells then -if _dist<_data.target.radius then -local _cr=_data.target.coord:GetRandomCoordinateInRadius(_data.target.radius) -local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt,self.illuMaxalt) -local _ci=COORDINATE:New(_cr.x,_alt,_cr.z) -_ci:IlluminationBomb(self.illuPower) -_destroyweapon=true -end -elseif _data.target.weapontype==ARTY.WeaponType.SmokeShells then -if _dist<_data.target.radius then -local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius) -_cr:Smoke(self.smokeColor) -_destroyweapon=true -end -end -if _destroyweapon then -self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.",self.groupname)) -_data.weapon:destroy() -return nil -else -local dt=0.02 -self:T3(self.lid..string.format("ARTY %s tracking weapon again in %.3f seconds",self.groupname,dt)) -return timer.getTime()+dt -end -else -local _impactcoord=COORDINATE:NewFromVec3(_lastpos) -self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.",self.groupname)) -if _data.target.weapontype==ARTY.WeaponType.TacticalNukes then -self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.",self.groupname)) -SCHEDULER:New(nil,ARTY._NuclearBlast,{self,_impactcoord},1.0) -end -return nil -end -end -local _tracknuke=self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0 -local _trackillu=self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0 -local _tracksmoke=self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0 -if _tracknuke or _trackillu or _tracksmoke then -self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.",self.groupname)) -local _peter={} -_peter.weapon=EventData.weapon -_peter.target=UTILS.DeepCopy(self.currentTarget) -timer.scheduleFunction(_TrackWeapon,_peter,timer.getTime()+2.0) -end -local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() -if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes then -self.Nukes=self.Nukes-1 -end -if self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells then -self.Nillu=self.Nillu-1 -end -if self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells then -self.Nsmoke=self.Nsmoke-1 -end -local _outofammo=false -if _nammo==0 then -self:T(self.lid..string.format("Group %s completely out of ammo.",self.groupname)) -_outofammo=true -end -local _partlyoutofammo=self:_CheckOutOfAmmo({self.currentTarget}) -local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) -self:T(self.lid..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d",self.groupname,_nammo,_nshells,_nrockets,_nmissiles)) -self:T(self.lid..string.format("Group %s uses weapontype %s for current target.",self.groupname,_weapontype)) -local _ceasefire=false -local _relocate=false -if self.Nshots>=self.currentTarget.nshells then -local text=string.format("Group %s stop firing on target %s.",self.groupname,self.currentTarget.name) -self:T(self.lid..text) -MESSAGE:New(text,5):ToAllIf(self.Debug) -_ceasefire=true -_relocate=self.relocateafterfire -end -if _outofammo or _partlyoutofammo then -_ceasefire=true -end -if _relocate then -self:_Relocate() -end -if _ceasefire then -self:CeaseFire(self.currentTarget) -end -else -self:E(self.lid..string.format("WARNING: No current target for group %s?!",self.groupname)) -end -end -end -end -function ARTY:onEvent(Event) -if Event==nil or Event.idx==nil then -self:T3("Skipping onEvent. Event or Event.idx unknown.") -return true -end -self:T2(string.format("Event captured = %s",tostring(self.groupname))) -self:T2(string.format("Event id = %s",tostring(Event.id))) -self:T2(string.format("Event time = %s",tostring(Event.time))) -self:T2(string.format("Event idx = %s",tostring(Event.idx))) -self:T2(string.format("Event coalition = %s",tostring(Event.coalition))) -self:T2(string.format("Event group id = %s",tostring(Event.groupID))) -self:T2(string.format("Event text = %s",tostring(Event.text))) -if Event.initiator~=nil then -local _unitname=Event.initiator:getName() -self:T2(string.format("Event ini unit name = %s",tostring(_unitname))) -end -if Event.id==world.event.S_EVENT_MARK_ADDED then -self:T2({event="S_EVENT_MARK_ADDED",battery=self.groupname,vec3=Event.pos}) -elseif Event.id==world.event.S_EVENT_MARK_CHANGE then -self:T({event="S_EVENT_MARK_CHANGE",battery=self.groupname,vec3=Event.pos}) -self:_OnEventMarkChange(Event) -elseif Event.id==world.event.S_EVENT_MARK_REMOVED then -self:T2({event="S_EVENT_MARK_REMOVED",battery=self.groupname,vec3=Event.pos}) -self:_OnEventMarkRemove(Event) -end -end -function ARTY:_OnEventMarkRemove(Event) -local batterycoalition=self.coalition -if Event.text~=nil and Event.text:find("BATTERY")then -local _cancelmove=false -local _canceltarget=false -local _name="" -local _id=nil -if Event.text:find("Marked Relocation")then -_cancelmove=true -_name=self:_MarkMoveName(Event.idx) -_id=self:_GetMoveIndexByName(_name) -elseif Event.text:find("Marked Target")then -_canceltarget=true -_name=self:_MarkTargetName(Event.idx) -_id=self:_GetTargetIndexByName(_name) -else -return -end -if _id==nil then -return -end -if(batterycoalition==Event.coalition and self.markkey==nil)or self.markkey~=nil then -local _validkey=self:_MarkerKeyAuthentification(Event.text) -if _validkey then -if _cancelmove then -if self.currentMove and self.currentMove.name==_name then -self.Controllable:ClearTasks() -self:Arrived() -else -self:RemoveMove(_name) -end -elseif _canceltarget then -if self.currentTarget and self.currentTarget.name==_name then -self:CeaseFire(self.currentTarget) -self:RemoveTarget(_name) -else -self:RemoveTarget(_name) -end -end -end -end -end -end -function ARTY:_OnEventMarkChange(Event) -if Event.text~=nil and Event.text:lower():find("arty")then -local vec3={y=Event.pos.y,x=Event.pos.x,z=Event.pos.z} -local _coord=COORDINATE:NewFromVec3(vec3) -_coord.y=_coord:GetLandHeight() -local batterycoalition=self.coalition -local batteryname=self.groupname -if(batterycoalition==Event.coalition and self.markkey==nil)or self.markkey~=nil then -local _assign=self:_Markertext(Event.text) -if _assign==nil or not(_assign.engage or _assign.move or _assign.request or _assign.cancel or _assign.set)then -self:T(self.lid..string.format("WARNING: %s, no keyword ENGAGE, MOVE, REQUEST, CANCEL or SET in mark text! Command will not be executed. Text:\n%s",self.groupname,Event.text)) -return -end -local _assigned=false -if _assign.everyone then -_assigned=true -else -for _,bat in pairs(_assign.battery)do -if self.groupname==bat then -_assigned=true -end -end -for _,alias in pairs(_assign.aliases)do -if self.alias==alias then -_assigned=true -end -end -for _,bat in pairs(_assign.cluster)do -for _,cluster in pairs(self.clusters)do -if cluster==bat then -_assigned=true -end -end -end -end -if not _assigned then -self:T3(self.lid..string.format("INFO: ARTY group %s was not addressed! Mark text:\n%s",self.groupname,Event.text)) -return -else -if self.Controllable and self.Controllable:IsAlive()then -else -self:T3(self.lid..string.format("INFO: ARTY group %s was addressed but is NOT alive! Mark text:\n%s",self.groupname,Event.text)) -return -end -end -if _assign.coord then -_coord=_assign.coord -end -local _validkey=self:_MarkerKeyAuthentification(Event.text) -if _assign.request and _validkey then -if _assign.requestammo then -self:_MarkRequestAmmo() -end -if _assign.requestmoves then -self:_MarkRequestMoves() -end -if _assign.requesttargets then -self:_MarkRequestTargets() -end -if _assign.requeststatus then -self:_MarkRequestStatus() -end -if _assign.requestrearming then -self:Rearm() -end -return -end -if _assign.cancel and _validkey then -if _assign.cancelmove and self.currentMove then -self.Controllable:ClearTasks() -self:Arrived() -elseif _assign.canceltarget and self.currentTarget then -self.currentTarget.engaged=self.currentTarget.engaged+1 -self:CeaseFire(self.currentTarget) -elseif _assign.cancelrearm and self:is("Rearming")then -local nammo=self:GetAmmo() -if nammo>0 then -self:Rearmed() -else -self:Winchester() -end -end -return -end -if _assign.set and _validkey then -if _assign.setrearmingplace and self.ismobile then -self:SetRearmingPlace(_coord) -_coord:RemoveMark(Event.idx) -_coord:MarkToCoalition(string.format("Rearming place for battery %s",self.groupname),self.coalition,false,string.format("New rearming place for battery %s defined.",self.groupname)) -if self.Debug then -_coord:SmokeOrange() -end -end -if _assign.setrearminggroup then -_coord:RemoveMark(Event.idx) -local rearminggroupcoord=_assign.setrearminggroup:GetCoordinate() -rearminggroupcoord:MarkToCoalition(string.format("Rearming group for battery %s",self.groupname),self.coalition,false,string.format("New rearming group for battery %s defined.",self.groupname)) -self:SetRearmingGroup(_assign.setrearminggroup) -if self.Debug then -rearminggroupcoord:SmokeOrange() -end -end -return -end -if _validkey then -_coord:RemoveMark(Event.idx) -local _id=UTILS._MarkID+1 -if _assign.move then -local _name=self:_MarkMoveName(_id) -local text=string.format("%s, received new relocation assignment.",self.alias) -text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) -MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug) -local _movename=self:AssignMoveCoord(_coord,_assign.time,_assign.speed,_assign.onroad,_assign.movecanceltarget,_name,true) -if _movename~=nil then -local _mid=self:_GetMoveIndexByName(_movename) -local _move=self.moves[_mid] -local clock=tostring(self:_SecondsToClock(_move.time)) -local _markertext=_movename..string.format(", Time=%s, Speed=%d km/h, Use Roads=%s.",clock,_move.speed,tostring(_move.onroad)) -local _randomcoord=_coord:GetRandomCoordinateInRadius(100) -_randomcoord:MarkToCoalition(_markertext,batterycoalition,self.markreadonly or _assign.readonly) -else -local text=string.format("%s, relocation not possible.",self.alias) -MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug) -end -else -local _name=self:_MarkTargetName(_id) -local text=string.format("%s, received new target assignment.",self.alias) -text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) -if _assign.time then -text=text..string.format("\nTime %s",_assign.time) -end -if _assign.prio then -text=text..string.format("\nPrio %d",_assign.prio) -end -if _assign.radius then -text=text..string.format("\nRadius %d m",_assign.radius) -end -if _assign.nshells then -text=text..string.format("\nShots %d",_assign.nshells) -end -if _assign.maxengage then -text=text..string.format("\nEngagements %d",_assign.maxengage) -end -if _assign.weapontype then -text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype)) -end -MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug) -local _targetname=self:AssignTargetCoord(_coord,_assign.prio,_assign.radius,_assign.nshells,_assign.maxengage,_assign.time,_assign.weapontype,_name,true) -if _targetname~=nil then -local _tid=self:_GetTargetIndexByName(_targetname) -local _target=self.targets[_tid] -local clock=tostring(self:_SecondsToClock(_target.time)) -local weapon=self:_WeaponTypeName(_target.weapontype) -local _markertext=_targetname..string.format(", Priority=%d, Radius=%d m, Shots=%d, Engagements=%d, Weapon=%s, Time=%s",_target.prio,_target.radius,_target.nshells,_target.maxengage,weapon,clock) -local _randomcoord=_coord:GetRandomCoordinateInRadius(250) -_randomcoord:MarkToCoalition(_markertext,batterycoalition,self.markreadonly or _assign.readonly) -end -end -end -end -end -end -function ARTY:OnEventDead(EventData) -self:F(EventData) -local _name=self.groupname -if EventData and EventData.IniGroupName and EventData.IniGroupName==_name then -local unitname=tostring(EventData.IniUnitName) -self:T(self.lid..string.format("%s: Captured dead event for unit %s.",_name,unitname)) -self:Dead(unitname) -end -end -function ARTY:onafterStatus(Controllable,From,Event,To) -self:_EventFromTo("onafterStatus",Event,From,To) -local nammo,nshells,nrockets,nmissiles=self:GetAmmo() -if self.iscargo and self.cargogroup then -if self.cargogroup:IsLoaded()and not self:is("InTransit")then -self:T(self.lid..string.format("Group %s has been loaded into a carrier and is now transported.",self.alias)) -self:Loaded() -elseif self.cargogroup:IsUnLoaded()then -self:T(self.lid..string.format("Group %s has been unloaded from the carrier.",self.alias)) -self:UnLoaded() -end -end -local fsmstate=self:GetState() -self:T(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d",fsmstate,nammo,nshells,self.Nsmoke,self.Nillu,self.Nukes,self.nukewarhead/1000000,nrockets,nmissiles)) -if self.Controllable and self.Controllable:IsAlive()then -if self.Debug then -self:_StatusReport() -end -if self:is("Moving")then -self:T2(self.lid..string.format("%s: Moving",Controllable:GetName())) -end -if self:is("Rearming")then -local _rearmed=self:_CheckRearmed() -if _rearmed then -self:T2(self.lid..string.format("%s: Rearming ==> Rearmed",Controllable:GetName())) -self:Rearmed() -end -end -if self:is("Rearmed")then -local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) -self:T2(self.lid..string.format("%s: Rearmed. Distance ARTY to InitalCoord = %d m",Controllable:GetName(),distance)) -if distance<=self.RearmingDistance then -self:T2(self.lid..string.format("%s: Rearmed ==> CombatReady",Controllable:GetName())) -self:CombatReady() -end -end -if self:is("Arrived")then -self:T2(self.lid..string.format("%s: Arrived ==> CombatReady",Controllable:GetName())) -self:CombatReady() -end -if self:is("Firing")then -self:_CheckShootingStarted() -end -self:_CheckTargetsInRange() -local notpossible={} -for i=1,#self.targets do -local _target=self.targets[i] -local possible=self:_CheckWeaponTypePossible(_target) -if not possible then -table.insert(notpossible,_target.name) -end -end -for _,targetname in pairs(notpossible)do -self:E(self.lid..string.format("%s: Removing target %s because requested weapon is not possible with this type of unit.",self.groupname,targetname)) -self:RemoveTarget(targetname) -end -local _timedTarget=self:_CheckTimedTargets() -local _normalTarget=self:_CheckNormalTargets() -local _move=self:_CheckMoves() -if _move then -self:Move(_move) -elseif _timedTarget then -if self.currentTarget then -self:CeaseFire(self.currentTarget) -end -self:OpenFire(_timedTarget) -elseif _normalTarget then -self:OpenFire(_normalTarget) -end -local gotsome=false -if#self.targets>0 then -for i=1,#self.targets do -local _target=self.targets[i] -if self:_CheckWeaponTypeAvailable(_target)>0 then -gotsome=true -end -end -else -gotsome=true -end -if(nammo==0 or not gotsome)and not(self:is("Moving")or self:is("Rearming")or self:is("OutOfAmmo"))then -self:Winchester() -end -if self:is("OutOfAmmo")then -self:T2(self.lid..string.format("%s: OutOfAmmo ==> Rearm ==> Rearming",Controllable:GetName())) -self:Rearm() -end -self:__Status(self.StatusInterval) -elseif self.iscargo then -if self.cargogroup and self.cargogroup:IsAlive()then -if self:is("InTransit")then -self:__Status(-5) -end -end -else -self:E(self.lid..string.format("Arty group %s is not alive!",self.groupname)) -end -end -function ARTY:onbeforeLoaded(Controllable,From,Event,To) -if self.currentTarget then -self:CeaseFire(self.currentTarget) -end -return true -end -function ARTY:onafterUnLoaded(Controllable,From,Event,To) -self:CombatReady() -end -function ARTY:onenterCombatReady(Controllable,From,Event,To) -self:_EventFromTo("onenterCombatReady",Event,From,To) -self:T3(self.lid..string.format("onenterComabReady, from=%s, event=%s, to=%s",From,Event,To)) -end -function ARTY:onbeforeOpenFire(Controllable,From,Event,To,target) -self:_EventFromTo("onbeforeOpenFire",Event,From,To) -if self.currentTarget then -self:E(self.lid..string.format("ERROR: Group %s already has a target %s!",self.groupname,self.currentTarget.name)) -return false -end -if not self:_TargetInRange(target)then -self:E(self.lid..string.format("ERROR: Group %s, target %s is out of range!",self.groupname,self.currentTarget.name)) -return false -end -local nfire=self:_CheckWeaponTypeAvailable(target) -target.nshells=math.min(target.nshells,nfire) -if target.nshells<1 then -local text=string.format("%s, no ammo left to engage target %s with selected weapon type %s.") -return false -end -return true -end -function ARTY:onafterOpenFire(Controllable,From,Event,To,target) -self:_EventFromTo("onafterOpenFire",Event,From,To) -local id=self:_GetTargetIndexByName(target.name) -if id then -self.targets[id].underfire=true -self.currentTarget=target -self.currentTarget.Tassigned=timer.getTime() -end -local range=Controllable:GetCoordinate():Get2DDistance(target.coord) -local Nammo,Nshells,Nrockets,Nmissiles=self:GetAmmo() -local nfire=Nammo -local _type="shots" -if target.weapontype==ARTY.WeaponType.Auto then -nfire=Nammo -_type="shots" -elseif target.weapontype==ARTY.WeaponType.Cannon then -nfire=Nshells -_type="shells" -elseif target.weapontype==ARTY.WeaponType.TacticalNukes then -nfire=self.Nukes -_type="nuclear shells" -elseif target.weapontype==ARTY.WeaponType.IlluminationShells then -nfire=self.Nillu -_type="illumination shells" -elseif target.weapontype==ARTY.WeaponType.SmokeShells then -nfire=self.Nsmoke -_type="smoke shells" -elseif target.weapontype==ARTY.WeaponType.Rockets then -nfire=Nrockets -_type="rockets" -elseif target.weapontype==ARTY.WeaponType.CruiseMissile then -nfire=Nmissiles -_type="cruise missiles" -end -target.nshells=math.min(target.nshells,nfire) -local text=string.format("%s, opening fire on target %s with %d %s. Distance %.1f km.",Controllable:GetName(),target.name,target.nshells,_type,range/1000) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report) -if target.attackgroup then -self:_AttackGroup(target) -else -self:_FireAtCoord(target.coord,target.radius,target.nshells,target.weapontype) -end -end -function ARTY:onafterCeaseFire(Controllable,From,Event,To,target) -self:_EventFromTo("onafterCeaseFire",Event,From,To) -if target then -local text=string.format("%s, ceasing fire on target %s.",Controllable:GetName(),target.name) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report) -local id=self:_GetTargetIndexByName(target.name) -if id then -if self.Nshots>0 then -self.targets[id].engaged=self.targets[id].engaged+1 -self.targets[id].time=nil -end -self.targets[id].underfire=false -end -if target.engaged>=target.maxengage then -self:RemoveTarget(target.name) -end -self.Controllable:OptionROEHoldFire() -self.Controllable:ClearTasks() -else -self:E(self.lid..string.format("ERROR: No target in cease fire for group %s.",self.groupname)) -end -self.Nshots=0 -self.currentTarget=nil -end -function ARTY:onafterWinchester(Controllable,From,Event,To) -self:_EventFromTo("onafterWinchester",Event,From,To) -local text=string.format("%s, winchester!",Controllable:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug) -end -function ARTY:onbeforeRearm(Controllable,From,Event,To) -self:_EventFromTo("onbeforeRearm",Event,From,To) -local _rearmed=self:_CheckRearmed() -if _rearmed then -self:T(self.lid..string.format("%s, group is already armed to the teeth. Rearming request denied!",self.groupname)) -return false -else -self:T(self.lid..string.format("%s, group might be rearmed.",self.groupname)) -end -if self.RearmingGroup and self.RearmingGroup:IsAlive()then -return true -elseif self.RearmingPlaceCoord then -return true -else -return false -end -end -function ARTY:onafterRearm(Controllable,From,Event,To) -self:_EventFromTo("onafterRearm",Event,From,To) -local coordARTY=self.Controllable:GetCoordinate() -self.InitialCoord=coordARTY -local coordRARM=nil -if self.RearmingGroup then -coordRARM=self.RearmingGroup:GetCoordinate() -self.RearmingGroupCoord=coordRARM -end -if self.RearmingGroup and self.RearmingPlaceCoord and self.ismobile then -local text=string.format("%s, %s, request rearming at rearming place.",Controllable:GetName(),self.RearmingGroup:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug) -local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) -local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) -if dA>self.RearmingDistance then -local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord,self.RearmingDistance/4,self.RearmingDistance/2) -self:AssignMoveCoord(_tocoord,nil,nil,self.RearmingArtyOnRoad,false,"REARMING MOVE TO REARMING PLACE",true) -end -if dR>self.RearmingDistance then -local ToCoord=self:_VicinityCoord(self.RearmingPlaceCoord,self.RearmingDistance/4,self.RearmingDistance/2) -self:_Move(self.RearmingGroup,ToCoord,self.RearmingGroupSpeed,self.RearmingGroupOnRoad) -end -elseif self.RearmingGroup then -local text=string.format("%s, %s, request rearming.",Controllable:GetName(),self.RearmingGroup:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug) -local distance=coordARTY:Get2DDistance(coordRARM) -if distance>self.RearmingDistance then -self:_Move(self.RearmingGroup,self:_VicinityCoord(coordARTY),self.RearmingGroupSpeed,self.RearmingGroupOnRoad) -end -elseif self.RearmingPlaceCoord then -local text=string.format("%s, moving to rearming place.",Controllable:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug) -local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) -if dA>self.RearmingDistance then -local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord) -self:AssignMoveCoord(_tocoord,nil,nil,self.RearmingArtyOnRoad,false,"REARMING MOVE TO REARMING PLACE",true) -end -end -end -function ARTY:onafterRearmed(Controllable,From,Event,To) -self:_EventFromTo("onafterRearmed",Event,From,To) -local text=string.format("%s, rearming complete.",Controllable:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug) -self.Nukes=self.Nukes0 -self.Nillu=self.Nillu0 -self.Nsmoke=self.Nsmoke0 -local dist=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) -if dist>self.RearmingDistance then -self:AssignMoveCoord(self.InitialCoord,nil,nil,self.RearmingArtyOnRoad,false,"REARMING MOVE REARMING COMPLETE",true) -end -if self.RearmingGroup and self.RearmingGroup:IsAlive()then -local d=self.RearmingGroup:GetCoordinate():Get2DDistance(self.RearmingGroupCoord) -if d>self.RearmingDistance then -self:_Move(self.RearmingGroup,self.RearmingGroupCoord,self.RearmingGroupSpeed,self.RearmingGroupOnRoad) -else -self.RearmingGroup:ClearTasks() -end -end -end -function ARTY:_CheckRearmed() -self:F2() -local nammo,nshells,nrockets,nmissiles=self:GetAmmo() -local units=self.Controllable:GetUnits() -local nunits=0 -if units then -nunits=#units -end -local FullAmmo=self.Nammo0*nunits/self.IniGroupStrength -local _rearmpc=nammo/FullAmmo*100 -if _rearmpc>1 then -local text=string.format("%s, rearming %d %% complete.",self.alias,_rearmpc) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug) -end -if nammo>=FullAmmo then -return true -else -return false -end -end -function ARTY:onbeforeMove(Controllable,From,Event,To,move) -self:_EventFromTo("onbeforeMove",Event,From,To) -if not self.ismobile then -return false -end -if self.currentTarget then -if move.cancel then -self:CeaseFire(self.currentTarget) -else -return false -end -end -return true -end -function ARTY:onafterMove(Controllable,From,Event,To,move) -self:_EventFromTo("onafterMove",Event,From,To) -self.Controllable:OptionAlarmStateGreen() -self.Controllable:OptionROEHoldFire() -local _Speed=math.min(move.speed,self.SpeedMax) -if self.Debug then -move.coord:SmokeRed() -end -self.currentMove=move -self:_Move(self.Controllable,move.coord,move.speed,move.onroad) -end -function ARTY:onafterArrived(Controllable,From,Event,To) -self:_EventFromTo("onafterArrived",Event,From,To) -self.Controllable:OptionAlarmStateAuto() -local text=string.format("%s, arrived at destination.",Controllable:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug) -if self.currentMove then -self:RemoveMove(self.currentMove.name) -self.currentMove=nil -end -end -function ARTY:onafterNewTarget(Controllable,From,Event,To,target) -self:_EventFromTo("onafterNewTarget",Event,From,To) -local text=string.format("Adding new target %s.",target.name) -MESSAGE:New(text,5):ToAllIf(self.Debug) -self:T(self.lid..text) -end -function ARTY:onafterNewMove(Controllable,From,Event,To,move) -self:_EventFromTo("onafterNewTarget",Event,From,To) -local text=string.format("Adding new move %s.",move.name) -MESSAGE:New(text,5):ToAllIf(self.Debug) -self:T(self.lid..text) -end -function ARTY:onafterDead(Controllable,From,Event,To,Unitname) -self:_EventFromTo("onafterDead",Event,From,To) -local nunits=self.Controllable:CountAliveUnits() -local text=string.format("%s, our unit %s just died! %d units left.",self.groupname,Unitname,nunits) -MESSAGE:New(text,5):ToAllIf(self.Debug) -self:I(self.lid..text) -if nunits==0 then -if self.currentTarget then -self:CeaseFire(self.currentTarget) -end -if self.respawnafterdeath then -if not self.respawning then -self.respawning=true -self:__Respawn(self.respawndelay or 1) -end -else -self:Stop() -end -end -end -function ARTY:onafterRespawn(Controllable,From,Event,To) -self:_EventFromTo("onafterRespawn",Event,From,To) -env.info("FF Respawning arty group") -local group=self.Controllable -self.Controllable=group:Respawn() -self.respawning=false -self:__Status(-1) -end -function ARTY:onafterStop(Controllable,From,Event,To) -self:_EventFromTo("onafterStop",Event,From,To) -self:I(self.lid..string.format("Stopping ARTY FSM for group %s.",tostring(Controllable:GetName()))) -if self.currentTarget then -self:CeaseFire(self.currentTarget) -end -self:UnHandleEvent(EVENTS.Shot) -self:UnHandleEvent(EVENTS.Dead) -end -function ARTY:_FireAtCoord(coord,radius,nshells,weapontype) -self:F({coord=coord,radius=radius,nshells=nshells}) -local group=self.Controllable -if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then -weapontype=ARTY.WeaponType.Cannon -end -group:OptionROEOpenFire() -local vec2=coord:GetVec2() -local fire=group:TaskFireAtPoint(vec2,radius,nshells,weapontype) -group:SetTask(fire) -end -function ARTY:_AttackGroup(target) -local group=self.Controllable -local weapontype=target.weapontype -if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then -weapontype=ARTY.WeaponType.Cannon -end -group:OptionROEOpenFire() -local targetgroup=GROUP:FindByName(target.name) -local fire=group:TaskAttackGroup(targetgroup,weapontype,AI.Task.WeaponExpend.ONE,1) -group:SetTask(fire) -end -function ARTY:_NuclearBlast(_coord) -local S0=self.nukewarhead -local R0=self.nukerange -local N0=self.nukefires -_coord:Explosion(S0) -_coord:BigSmokeAndFireHuge() -local _fires={} -for i=1,N0 do -local _fire=_coord:GetRandomCoordinateInRadius(R0) -local _dist=_fire:Get2DDistance(_coord) -table.insert(_fires,{distance=_dist,coord=_fire}) -end -local _sort=function(a,b)return a.distance_nmax -if _gotit then -self:AssignMoveCoord(_new,nil,nil,false,false,"RELOCATION MOVE AFTER FIRING") -end -end -function ARTY:GetAmmo(display) -self:F3({display=display}) -if display==nil then -display=false -end -local nammo=0 -local nshells=0 -local nrockets=0 -local nmissiles=0 -local units=self.Controllable:GetUnits() -if units==nil then -return nammo,nshells,nrockets,nmissiles -end -for _,unit in pairs(units)do -if unit and unit:IsAlive()then -local text=string.format("ARTY group %s - unit %s:\n",self.groupname,unit:GetName()) -local ammotable=unit:GetAmmo() -if ammotable~=nil then -local weapons=#ammotable -if display then -self:I(self.lid..string.format("Number of weapons %d.",weapons)) -self:I({ammotable=ammotable}) -self:I(self.lid.."Ammotable:") -for id,bla in pairs(ammotable)do -self:I({id=id,ammo=bla}) -end -end -for w=1,weapons do -local Nammo=ammotable[w]["count"] -local Tammo=ammotable[w]["desc"]["typeName"] -local _weaponString=self:_split(Tammo,"%.") -local _weaponName=_weaponString[#_weaponString] -local Category=ammotable[w].desc.category -local MissileCategory=nil -if Category==Weapon.Category.MISSILE then -MissileCategory=ammotable[w].desc.missileCategory -end -local _gotshell=false -if#self.ammoshells>0 then -for _,_type in pairs(self.ammoshells)do -if string.match(Tammo,_type)and Category==Weapon.Category.SHELL then -_gotshell=true -end -end -else -if Category==Weapon.Category.SHELL then -_gotshell=true -end -end -local _gotrocket=false -if#self.ammorockets>0 then -for _,_type in pairs(self.ammorockets)do -if string.match(Tammo,_type)and Category==Weapon.Category.ROCKET then -_gotrocket=true -end -end -else -if Category==Weapon.Category.ROCKET then -_gotrocket=true -end -end -local _gotmissile=false -if#self.ammomissiles>0 then -for _,_type in pairs(self.ammomissiles)do -if string.match(Tammo,_type)and Category==Weapon.Category.MISSILE then -_gotmissile=true -end -end -else -if Category==Weapon.Category.MISSILE then -_gotmissile=true -end -end -if _gotshell then -nshells=nshells+Nammo -text=text..string.format("- %d shells of type %s\n",Nammo,_weaponName) -elseif _gotrocket then -nrockets=nrockets+Nammo -text=text..string.format("- %d rockets of type %s\n",Nammo,_weaponName) -elseif _gotmissile then -if MissileCategory==Weapon.MissileCategory.CRUISE then -nmissiles=nmissiles+Nammo -end -text=text..string.format("- %d %s missiles of type %s\n",Nammo,self:_MissileCategoryName(MissileCategory),_weaponName) -else -text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n",Nammo,Tammo,Category,tostring(MissileCategory)) -end -end -end -if display then -self:I(self.lid..text) -else -self:T3(self.lid..text) -end -MESSAGE:New(text,10):ToAllIf(display) -end -end -nammo=nshells+nrockets+nmissiles -return nammo,nshells,nrockets,nmissiles -end -function ARTY:_MissileCategoryName(categorynumber) -local cat="unknown" -if categorynumber==Weapon.MissileCategory.AAM then -cat="air-to-air" -elseif categorynumber==Weapon.MissileCategory.SAM then -cat="surface-to-air" -elseif categorynumber==Weapon.MissileCategory.BM then -cat="ballistic" -elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then -cat="anti-ship" -elseif categorynumber==Weapon.MissileCategory.CRUISE then -cat="cruise" -elseif categorynumber==Weapon.MissileCategory.OTHER then -cat="other" -end -return cat -end -function ARTY:_MarkerKeyAuthentification(text) -local batterycoalition=self.coalition -local mykey=nil -if self.markkey~=nil then -local keywords=self:_split(text,",") -for _,key in pairs(keywords)do -local s=self:_split(key," ") -local val=s[2] -if key:lower():find("key")then -mykey=tonumber(val) -self:T(self.lid..string.format("Authorisation Key=%s.",val)) -end -end -end -local _validkey=true -if self.markkey~=nil then -_validkey=false -if mykey~=nil then -_validkey=self.markkey==mykey -end -self:T2(self.lid..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s",self.groupname,tostring(self.markkey),tostring(mykey),tostring(_validkey))) -local text="" -if mykey==nil then -text=string.format("%s, authorization required but did not receive a key!",self.alias) -elseif _validkey==false then -text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!",self.alias,tostring(mykey)) -elseif _validkey==true then -text=string.format("%s, authentification successful!",self.alias) -end -MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug) -end -return _validkey -end -function ARTY:_Markertext(text) -self:F(text) -local assignment={} -assignment.battery={} -assignment.aliases={} -assignment.cluster={} -assignment.everyone=false -assignment.move=false -assignment.engage=false -assignment.request=false -assignment.cancel=false -assignment.set=false -assignment.readonly=false -assignment.movecanceltarget=false -assignment.cancelmove=false -assignment.canceltarget=false -assignment.cancelrearm=false -assignment.setrearmingplace=false -assignment.setrearminggroup=false -if text:lower():find("arty engage")or text:lower():find("arty attack")then -assignment.engage=true -elseif text:lower():find("arty move")or text:lower():find("arty relocate")then -assignment.move=true -elseif text:lower():find("arty request")then -assignment.request=true -elseif text:lower():find("arty cancel")then -assignment.cancel=true -elseif text:lower():find("arty set")then -assignment.set=true -else -self:E(self.lid..'ERROR: Neither "ARTY ENGAGE" nor "ARTY MOVE" nor "ARTY RELOCATE" nor "ARTY REQUEST" nor "ARTY CANCEL" nor "ARTY SET" keyword specified!') -return nil -end -local keywords=self:_split(text,",") -self:T({keywords=keywords}) -for _,keyphrase in pairs(keywords)do -local str=self:_split(keyphrase," ") -local key=str[1] -local val=str[2] -self:T3(self.lid..string.format("%s, keyphrase = %s, key = %s, val = %s",self.groupname,tostring(keyphrase),tostring(key),tostring(val))) -if key:lower():find("battery")then -local v=self:_split(keyphrase,'"') -for i=2,#v,2 do -table.insert(assignment.battery,v[i]) -self:T2(self.lid..string.format("Key Battery=%s.",v[i])) -end -elseif key:lower():find("alias")then -local v=self:_split(keyphrase,'"') -for i=2,#v,2 do -table.insert(assignment.aliases,v[i]) -self:T2(self.lid..string.format("Key Aliases=%s.",v[i])) -end -elseif key:lower():find("cluster")then -local v=self:_split(keyphrase,'"') -for i=2,#v,2 do -table.insert(assignment.cluster,v[i]) -self:T2(self.lid..string.format("Key Cluster=%s.",v[i])) -end -elseif keyphrase:lower():find("everyone")or keyphrase:lower():find("all batteries")or keyphrase:lower():find("allbatteries")then -assignment.everyone=true -self:T(self.lid..string.format("Key Everyone=true.")) -elseif keyphrase:lower():find("irrevocable")or keyphrase:lower():find("readonly")then -assignment.readonly=true -self:T2(self.lid..string.format("Key Readonly=true.")) -elseif(assignment.engage or assignment.move)and key:lower():find("time")then -if val:lower():find("now")then -assignment.time=self:_SecondsToClock(timer.getTime0()+2) -else -assignment.time=val -end -self:T2(self.lid..string.format("Key Time=%s.",val)) -elseif assignment.engage and key:lower():find("shot")then -assignment.nshells=tonumber(val) -self:T(self.lid..string.format("Key Shot=%s.",val)) -elseif assignment.engage and key:lower():find("prio")then -assignment.prio=tonumber(val) -self:T2(string.format("Key Prio=%s.",val)) -elseif assignment.engage and key:lower():find("maxengage")then -assignment.maxengage=tonumber(val) -self:T2(self.lid..string.format("Key Maxengage=%s.",val)) -elseif assignment.engage and key:lower():find("radius")then -assignment.radius=tonumber(val) -self:T2(self.lid..string.format("Key Radius=%s.",val)) -elseif assignment.engage and key:lower():find("weapon")then -if val:lower():find("cannon")then -assignment.weapontype=ARTY.WeaponType.Cannon -elseif val:lower():find("rocket")then -assignment.weapontype=ARTY.WeaponType.Rockets -elseif val:lower():find("missile")then -assignment.weapontype=ARTY.WeaponType.CruiseMissile -elseif val:lower():find("nuke")then -assignment.weapontype=ARTY.WeaponType.TacticalNukes -elseif val:lower():find("illu")then -assignment.weapontype=ARTY.WeaponType.IlluminationShells -elseif val:lower():find("smoke")then -assignment.weapontype=ARTY.WeaponType.SmokeShells -else -assignment.weapontype=ARTY.WeaponType.Auto -end -self:T2(self.lid..string.format("Key Weapon=%s.",val)) -elseif(assignment.move or assignment.set)and key:lower():find("speed")then -assignment.speed=tonumber(val) -self:T2(self.lid..string.format("Key Speed=%s.",val)) -elseif(assignment.move or assignment.set)and(keyphrase:lower():find("on road")or keyphrase:lower():find("onroad")or keyphrase:lower():find("use road"))then -assignment.onroad=true -self:T2(self.lid..string.format("Key Onroad=true.")) -elseif assignment.move and(keyphrase:lower():find("cancel target")or keyphrase:lower():find("canceltarget"))then -assignment.movecanceltarget=true -self:T2(self.lid..string.format("Key Cancel Target (before move)=true.")) -elseif assignment.request and keyphrase:lower():find("rearm")then -assignment.requestrearming=true -self:T2(self.lid..string.format("Key Request Rearming=true.")) -elseif assignment.request and keyphrase:lower():find("ammo")then -assignment.requestammo=true -self:T2(self.lid..string.format("Key Request Ammo=true.")) -elseif assignment.request and keyphrase:lower():find("target")then -assignment.requesttargets=true -self:T2(self.lid..string.format("Key Request Targets=true.")) -elseif assignment.request and keyphrase:lower():find("status")then -assignment.requeststatus=true -self:T2(self.lid..string.format("Key Request Status=true.")) -elseif assignment.request and(keyphrase:lower():find("move")or keyphrase:lower():find("relocation"))then -assignment.requestmoves=true -self:T2(self.lid..string.format("Key Request Moves=true.")) -elseif assignment.cancel and(keyphrase:lower():find("engagement")or keyphrase:lower():find("attack")or keyphrase:lower():find("target"))then -assignment.canceltarget=true -self:T2(self.lid..string.format("Key Cancel Target=true.")) -elseif assignment.cancel and(keyphrase:lower():find("move")or keyphrase:lower():find("relocation"))then -assignment.cancelmove=true -self:T2(self.lid..string.format("Key Cancel Move=true.")) -elseif assignment.cancel and keyphrase:lower():find("rearm")then -assignment.cancelrearm=true -self:T2(self.lid..string.format("Key Cancel Rearm=true.")) -elseif assignment.set and keyphrase:lower():find("rearming place")then -assignment.setrearmingplace=true -self:T(self.lid..string.format("Key Set Rearming Place=true.")) -elseif assignment.set and keyphrase:lower():find("rearming group")then -local v=self:_split(keyphrase,'"') -local groupname=v[2] -local group=GROUP:FindByName(groupname) -if group and group:IsAlive()then -assignment.setrearminggroup=group -end -self:T2(self.lid..string.format("Key Set Rearming Group = %s.",tostring(groupname))) -elseif key:lower():find("lldms")then -local _flat="%d+:%d+:%d+%s*[N,S]" -local _flon="%d+:%d+:%d+%s*[W,E]" -local _lat=keyphrase:match(_flat) -local _lon=keyphrase:match(_flon) -self:T2(self.lid..string.format("Key LLDMS: lat=%s, long=%s format=DMS",_lat,_lon)) -if _lat and _lon then -local _latitude,_longitude=self:_LLDMS2DD(_lat,_lon) -self:T2(self.lid..string.format("Key LLDMS: lat=%.3f, long=%.3f format=DD",_latitude,_longitude)) -if _latitude and _longitude then -assignment.coord=COORDINATE:NewFromLLDD(_latitude,_longitude) -end -end -end -end -return assignment -end -function ARTY:_MarkRequestAmmo() -self:GetAmmo(true) -end -function ARTY:_MarkRequestStatus() -self:_StatusReport(true) -end -function ARTY:_MarkRequestMoves() -local text=string.format("%s, relocations:",self.groupname) -if#self.moves>0 then -for _,move in pairs(self.moves)do -if self.currentMove and move.name==self.currentMove.name then -text=text..string.format("\n- %s (current)",self:_MoveInfo(move)) -else -text=text..string.format("\n- %s",self:_MoveInfo(move)) -end -end -else -text=text..string.format("\n- no queued relocations") -end -MESSAGE:New(text,20):Clear():ToCoalition(self.coalition) -end -function ARTY:_MarkRequestTargets() -local text=string.format("%s, targets:",self.groupname) -if#self.targets>0 then -for _,target in pairs(self.targets)do -if self.currentTarget and target.name==self.currentTarget.name then -text=text..string.format("\n- %s (current)",self:_TargetInfo(target)) -else -text=text..string.format("\n- %s",self:_TargetInfo(target)) -end -end -else -text=text..string.format("\n- no queued targets") -end -MESSAGE:New(text,20):Clear():ToCoalition(self.coalition) -end -function ARTY:_MarkTargetName(markerid) -return string.format("BATTERY=%s, Marked Target ID=%d",self.groupname,markerid) -end -function ARTY:_MarkMoveName(markerid) -return string.format("BATTERY=%s, Marked Relocation ID=%d",self.groupname,markerid) -end -function ARTY:_GetMarkIDfromName(name) -local keywords=self:_split(name,",") -local battery=nil -local markTID=nil -local markMID=nil -for _,key in pairs(keywords)do -local str=self:_split(key,"=") -local par=str[1] -local val=str[2] -if par:find("BATTERY")then -battery=val -end -if par:find("Marked Target ID")then -markTID=tonumber(val) -end -if par:find("Marked Relocation ID")then -markMID=tonumber(val) -end -end -return battery,markTID,markMID -end -function ARTY:_SortTargetQueuePrio() -self:F2() -local function _sort(a,b) -return(a.engaged_target.engaged and self:_TargetInRange(_target)and self:_CheckWeaponTypeAvailable(_target)>0 then -self:T2(self.lid..string.format("Found NORMAL target %s",self:_TargetInfo(_target))) -return _target -end -end -return nil -end -function ARTY:_CheckTimedTargets() -self:F3() -local Tnow=timer.getAbsTime() -self:_SortQueueTime(self.targets) -if self:is("Rearming")then -return nil -end -for i=1,#self.targets do -local _target=self.targets[i] -self:T3(self.lid..string.format("Check TIMED target %d: %s",i,self:_TargetInfo(_target))) -if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target)and self:_CheckWeaponTypeAvailable(_target)>0 then -if self.currentTarget then -if self.currentTarget.prio>_target.prio then -self:T2(self.lid..string.format("Found TIMED HIGH PRIO target %s.",self:_TargetInfo(_target))) -return _target -end -else -self:T2(self.lid..string.format("Found TIMED target %s.",self:_TargetInfo(_target))) -return _target -end -end -end -return nil -end -function ARTY:_CheckMoves() -self:F3() -local Tnow=timer.getAbsTime() -self:_SortQueueTime(self.moves) -local firing=false -if self.currentTarget then -firing=true -end -for i=1,#self.moves do -local _move=self.moves[i] -if string.find(_move.name,"REARMING MOVE")and((self.currentMove and self.currentMove.name~=_move.name)or self.currentMove==nil)then -return _move -elseif(Tnow>=_move.time)and(firing==false or _move.cancel)and(not self.currentMove)and(not self:is("Rearming"))then -return _move -end -end -return nil -end -function ARTY:_CheckShootingStarted() -self:F2() -if self.currentTarget then -local Tnow=timer.getTime() -local name=self.currentTarget.name -local dt=Tnow-self.currentTarget.Tassigned -if self.Nshots==0 then -self:T(self.lid..string.format("%s, waiting for %d seconds for first shot on target %s.",self.groupname,dt,name)) -end -if dt>self.WaitForShotTime and(self.Nshots==0 or self.currentTarget.nshells>=self.Nshots)then -self:T(self.lid..string.format("%s, no shot event after %d seconds. Removing current target %s from list.",self.groupname,self.WaitForShotTime,name)) -self:CeaseFire(self.currentTarget) -self:RemoveTarget(name) -end -end -end -function ARTY:_GetTargetIndexByName(name) -self:F2(name) -for i=1,#self.targets do -local targetname=self.targets[i].name -self:T3(self.lid..string.format("Have target with name %s. Index = %d",targetname,i)) -if targetname==name then -self:T2(self.lid..string.format("Found target with name %s. Index = %d",name,i)) -return i -end -end -self:T2(self.lid..string.format("WARNING: Target with name %s could not be found. (This can happen.)",name)) -return nil -end -function ARTY:_GetMoveIndexByName(name) -self:F2(name) -for i=1,#self.moves do -local movename=self.moves[i].name -self:T3(self.lid..string.format("Have move with name %s. Index = %d",movename,i)) -if movename==name then -self:T2(self.lid..string.format("Found move with name %s. Index = %d",name,i)) -return i -end -end -self:T2(self.lid..string.format("WARNING: Move with name %s could not be found. (This can happen.)",name)) -return nil -end -function ARTY:_CheckOutOfAmmo(targets) -local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() -local _partlyoutofammo=false -for _,Target in pairs(targets)do -if Target.weapontype==ARTY.WeaponType.Auto and _nammo==0 then -self:T(self.lid..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.",self.groupname,Target.name)) -_partlyoutofammo=true -elseif Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then -self:T(self.lid..string.format("Group %s, cannons requested for target %s but shells empty.",self.groupname,Target.name)) -_partlyoutofammo=true -elseif Target.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then -self:T(self.lid..string.format("Group %s, tactical nukes requested for target %s but nukes empty.",self.groupname,Target.name)) -_partlyoutofammo=true -elseif Target.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu<=0 then -self:T(self.lid..string.format("Group %s, illumination shells requested for target %s but illumination shells empty.",self.groupname,Target.name)) -_partlyoutofammo=true -elseif Target.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke<=0 then -self:T(self.lid..string.format("Group %s, smoke shells requested for target %s but smoke shells empty.",self.groupname,Target.name)) -_partlyoutofammo=true -elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then -self:T(self.lid..string.format("Group %s, rockets requested for target %s but rockets empty.",self.groupname,Target.name)) -_partlyoutofammo=true -elseif Target.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then -self:T(self.lid..string.format("Group %s, cruise missiles requested for target %s but all missiles empty.",self.groupname,Target.name)) -_partlyoutofammo=true -end -end -return _partlyoutofammo -end -function ARTY:_CheckWeaponTypeAvailable(target) -local Nammo,Nshells,Nrockets,Nmissiles=self:GetAmmo() -local nfire=Nammo -if target.weapontype==ARTY.WeaponType.Auto then -nfire=Nammo -elseif target.weapontype==ARTY.WeaponType.Cannon then -nfire=Nshells -elseif target.weapontype==ARTY.WeaponType.TacticalNukes then -nfire=self.Nukes -elseif target.weapontype==ARTY.WeaponType.IlluminationShells then -nfire=self.Nillu -elseif target.weapontype==ARTY.WeaponType.SmokeShells then -nfire=self.Nsmoke -elseif target.weapontype==ARTY.WeaponType.Rockets then -nfire=Nrockets -elseif target.weapontype==ARTY.WeaponType.CruiseMissile then -nfire=Nmissiles -end -return nfire -end -function ARTY:_CheckWeaponTypePossible(target) -local possible=false -if target.weapontype==ARTY.WeaponType.Auto then -possible=self.Nammo0>0 -elseif target.weapontype==ARTY.WeaponType.Cannon then -possible=self.Nshells0>0 -elseif target.weapontype==ARTY.WeaponType.TacticalNukes then -possible=self.Nukes0>0 -elseif target.weapontype==ARTY.WeaponType.IlluminationShells then -possible=self.Nillu0>0 -elseif target.weapontype==ARTY.WeaponType.SmokeShells then -possible=self.Nsmoke0>0 -elseif target.weapontype==ARTY.WeaponType.Rockets then -possible=self.Nrockets0>0 -elseif target.weapontype==ARTY.WeaponType.CruiseMissile then -possible=self.Nmissiles0>0 -end -return possible -end -function ARTY:_CheckName(givennames,name,makeunique) -self:F2({givennames=givennames,name=name}) -local newname=name -local counter=1 -local n=1 -local nmax=100 -if makeunique==nil then -makeunique=true -end -repeat -local _unique=true -for _,_target in pairs(givennames)do -local _givenname=_target.name -if _givenname==newname then -_unique=false -end -self:T3(self.lid..string.format("%d: givenname = %s, newname=%s, unique = %s, makeunique = %s",n,tostring(_givenname),newname,tostring(_unique),tostring(makeunique))) -end -if _unique==false and makeunique==true then -newname=string.format("%s #%02d",name,counter) -counter=counter+1 -end -if _unique==false and makeunique==false then -self:T3(self.lid..string.format("Name %s is not unique. Return false.",tostring(newname))) -return name,false -end -n=n+1 -until(_unique or n==nmax) -self:T3(self.lid..string.format("Original name %s, new name = %s",name,newname)) -return newname,true -end -function ARTY:_TargetInRange(target,message) -self:F3(target) -if message==nil then -message=false -end -self:T3({controllable=self.Controllable,targetcoord=target.coord}) -local _dist=self.Controllable:GetCoordinate():Get2DDistance(target.coord) -local _inrange=true -local _tooclose=false -local _toofar=false -local text="" -if _distself.maxrange then -_inrange=false -_toofar=true -text=string.format("%s, target is out of range. Distance of %.1f km is greater than max range of %.1f km.",self.alias,_dist/1000,self.maxrange/1000) -end -if not _inrange then -self:T(self.lid..text) -MESSAGE:New(text,5):ToCoalitionIf(self.coalition,(self.report and message)or(self.Debug and message)) -end -local _remove=false -if not(self.ismobile or self.iscargo)and _inrange==false then -_remove=true -end -return _inrange,_toofar,_tooclose,_remove -end -function ARTY:_WeaponTypeName(tnumber) -self:F2(tnumber) -local name="unknown" -if tnumber==ARTY.WeaponType.Auto then -name="Auto" -elseif tnumber==ARTY.WeaponType.Cannon then -name="Cannons" -elseif tnumber==ARTY.WeaponType.Rockets then -name="Rockets" -elseif tnumber==ARTY.WeaponType.CruiseMissile then -name="Cruise Missiles" -elseif tnumber==ARTY.WeaponType.TacticalNukes then -name="Tactical Nukes" -elseif tnumber==ARTY.WeaponType.IlluminationShells then -name="Illumination Shells" -elseif tnumber==ARTY.WeaponType.SmokeShells then -name="Smoke Shells" -end -return name -end -function ARTY:_VicinityCoord(coord,rmin,rmax) -self:F2({coord=coord,rmin=rmin,rmax=rmax}) -rmin=rmin or 20 -rmax=rmax or 80 -local vec2=coord:GetRandomVec2InRadius(rmax,rmin) -local pops=COORDINATE:NewFromVec2(vec2) -self:T3(self.lid..string.format("Vicinity distance = %d (rmin=%d, rmax=%d)",pops:Get2DDistance(coord),rmin,rmax)) -return pops -end -function ARTY:_EventFromTo(BA,Event,From,To) -local text=string.format("%s: %s EVENT %s: %s --> %s",BA,self.groupname,Event,From,To) -self:T3(self.lid..text) -end -function ARTY:_split(str,sep) -self:F3({str=str,sep=sep}) -local result={} -local regex=("([^%s]+)"):format(sep) -for each in str:gmatch(regex)do -table.insert(result,each) -end -return result -end -function ARTY:_TargetInfo(target) -local clock=tostring(self:_SecondsToClock(target.time)) -local weapon=self:_WeaponTypeName(target.weapontype) -local _underfire=tostring(target.underfire) -return string.format("%s: prio=%d, radius=%d, nshells=%d, engaged=%d/%d, weapontype=%s, time=%s, underfire=%s, attackgroup=%s", -target.name,target.prio,target.radius,target.nshells,target.engaged,target.maxengage,weapon,clock,_underfire,tostring(target.attackgroup)) -end -function ARTY:_MoveInfo(move) -self:F3(move) -local _clock=self:_SecondsToClock(move.time) -return string.format("%s: time=%s, speed=%d, onroad=%s, cancel=%s",move.name,_clock,move.speed,tostring(move.onroad),tostring(move.cancel)) -end -function ARTY:_LLDMS2DD(l1,l2) -self:F2(l1,l2) -local _latlong={l1,l2} -local _latitude=nil -local _longitude=nil -for _,ll in pairs(_latlong)do -local _format="%d+:%d+:%d+" -local _ldms=ll:match(_format) -if _ldms then -local _dms=self:_split(_ldms,":") -local _deg=tonumber(_dms[1]) -local _min=tonumber(_dms[2]) -local _sec=tonumber(_dms[3]) -local function DMS2DD(d,m,s) -return d+m/60+s/3600 -end -if ll:match("N")then -_latitude=DMS2DD(_deg,_min,_sec) -elseif ll:match("S")then -_latitude=-DMS2DD(_deg,_min,_sec) -elseif ll:match("W")then -_longitude=-DMS2DD(_deg,_min,_sec) -elseif ll:match("E")then -_longitude=DMS2DD(_deg,_min,_sec) -end -local text=string.format("DMS %02d Deg %02d min %02d sec",_deg,_min,_sec) -self:T2(self.lid..text) -end -end -local text=string.format("\nLatitude %s",tostring(_latitude)) -text=text..string.format("\nLongitude %s",tostring(_longitude)) -self:T2(self.lid..text) -return _latitude,_longitude -end -function ARTY:_SecondsToClock(seconds) -self:F3({seconds=seconds}) -if seconds==nil then -return nil -end -local seconds=tonumber(seconds) -local _seconds=seconds%(60*60*24) -if seconds<=0 then -return nil -else -local hours=string.format("%02.f",math.floor(_seconds/3600)) -local mins=string.format("%02.f",math.floor(_seconds/60-(hours*60))) -local secs=string.format("%02.f",math.floor(_seconds-hours*3600-mins*60)) -local days=string.format("%d",seconds/(60*60*24)) -return hours..":"..mins..":"..secs.."+"..days -end -end -function ARTY:_ClockToSeconds(clock) -self:F3({clock=clock}) -if clock==nil then -return nil -end -local seconds=0 -local dsplit=self:_split(clock,"+") -if#dsplit>1 then -seconds=seconds+tonumber(dsplit[2])*60*60*24 -end -local tsplit=self:_split(dsplit[1],":") -local i=1 -for _,time in ipairs(tsplit)do -if i==1 then -seconds=seconds+tonumber(time)*60*60 -elseif i==2 then -seconds=seconds+tonumber(time)*60 -elseif i==3 then -seconds=seconds+tonumber(time) -end -i=i+1 -end -self:T3(self.lid..string.format("Clock %s = %d seconds",clock,seconds)) -return seconds -end -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, -} -SUPPRESSION.ROE={ -Hold="Weapon Hold", -Free="Weapon Free", -Return="Return Fire", -} -SUPPRESSION.AlarmState={ -Auto="Auto", -Green="Green", -Red="Red", -} -SUPPRESSION.MenuF10=nil -SUPPRESSION.version="0.9.3" -function SUPPRESSION:New(group) -local self=BASE:Inherit(self,FSM_CONTROLLABLE:New()) -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 -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 -self:SetControllable(group) -self.DCSdesc=group:GetDCSDesc(1) -self.SpeedMax=group:GetSpeedMax() -self.Speed=self.SpeedMax -self.IsInfantry=group:GetUnit(1):HasAttribute("Infantry") -self.Type=group:GetTypeName() -self.IniGroupStrength=#group:GetUnits() -self:SetDefaultROE("Free") -self:SetDefaultAlarmState("Auto") -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("*","OutOfAmmo","*") -self:AddTransition("*","Dead","*") -self:AddTransition("*","Stop","Stopped") -self:AddTransition("TakingCover","Hit","TakingCover") -self:AddTransition("FallingBack","Hit","FallingBack") -return self -end -function SUPPRESSION:SetSuppressionTime(Tave,Tmin,Tmax) -self:F({Tave=Tave,Tmin=Tmin,Tmax=Tmax}) -self.Tsuppress_min=Tmin or self.Tsuppress_min -self.Tsuppress_min=math.max(self.Tsuppress_min,1) -self.Tsuppress_max=Tmax or self.Tsuppress_max -self.Tsuppress_max=math.max(self.Tsuppress_max,self.Tsuppress_min) -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 -function SUPPRESSION:SetRetreatZone(zone) -self:F({zone=zone}) -self.RetreatZone=zone -end -function SUPPRESSION:DebugOn() -self:F() -self.Debug=true -end -function SUPPRESSION:FlareOn() -self:F() -self.flare=true -end -function SUPPRESSION:SmokeOn() -self:F() -self.smoke=true -end -function SUPPRESSION:SetFormation(formation) -self:F(formation) -self.Formation=formation or"Vee" -end -function SUPPRESSION:SetSpeed(speed) -self:F(speed) -self.Speed=speed or self.SpeedMax -self.Speed=math.min(self.Speed,self.SpeedMax) -end -function SUPPRESSION:Fallback(switch) -self:F(switch) -if switch==nil then -switch=true -end -self.FallbackON=switch -end -function SUPPRESSION:SetFallbackDistance(distance) -self:F(distance) -self.FallbackDist=distance -end -function SUPPRESSION:SetFallbackWait(time) -self:F(time) -self.FallbackWait=time -end -function SUPPRESSION:Takecover(switch) -self:F(switch) -if switch==nil then -switch=true -end -self.TakecoverON=switch -end -function SUPPRESSION:SetTakecoverWait(time) -self:F(time) -self.TakecoverWait=time -end -function SUPPRESSION:SetTakecoverRange(range) -self:F(range) -self.TakecoverRange=range -end -function SUPPRESSION:SetTakecoverPlace(Hideout) -self.hideout=Hideout -end -function SUPPRESSION:SetMinimumFleeProbability(probability) -self:F(probability) -self.PminFlee=probability or 10 -end -function SUPPRESSION:SetMaximumFleeProbability(probability) -self:F(probability) -self.PmaxFlee=probability or 90 -end -function SUPPRESSION:SetRetreatDamage(damage) -self:F(damage) -self.RetreatDamage=damage or 50 -end -function SUPPRESSION:SetRetreatWait(time) -self:F(time) -self.RetreatWait=time or 7200 -end -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 -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 -function SUPPRESSION:MenuOn(switch) -self:F(switch) -if switch==nil then -switch=true -end -self.MenuON=switch -end -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 -function SUPPRESSION:OrderFallBack() -local group=self.Controllable -local vicinity=group:GetCoordinate():GetRandomVec2InRadius(150,100) -local coord=COORDINATE:NewFromVec2(vicinity) -self:FallBack(self.Controllable) -end -function SUPPRESSION:OrderTakeCover() -local Hideout=self.hideout -if self.hideout==nil then -Hideout=self:_SearchHideout() -end -self:TakeCover(Hideout) -end -function SUPPRESSION:OrderRetreat() -self:Retreat() -end -function SUPPRESSION:StatusReport(message) -local group=self.Controllable -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=group:GetAmmunition() -local detectedG=group:GetDetectedGroupSet():CountAlive() -local detectedU=group:GetDetectedUnitSet():Count() -local text=string.format("State %s, Units=%d/%d, ROE=%s, AlarmState=%s, Hits=%d, Life(min/max/ave/ave0)=%d/%d/%d/%d, Total Ammo=%d, Detected=%d/%d", -self:GetState(),nunits,self.IniGroupStrength,self.CurrentROE,self.CurrentAlarmState,self.Nhit,life_min,life_max,life_ave,life_ave0,ammotot,detectedG,detectedU) -MESSAGE:New(text,10):ToAllIf(message or self.Debug) -self:I(self.lid..text) -end -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 -if self.RetreatDamage==nil then -if self.RetreatZone then -if self.IniGroupStrength==1 then -self.RetreatDamage=60.0 -elseif self.IniGroupStrength==2 then -self.RetreatDamage=50.0 -else -self.RetreatDamage=66.5 -end -else -self.RetreatDamage=100 -end -end -if self.MenuON then -if not SUPPRESSION.MenuF10 then -SUPPRESSION.MenuF10=MENU_MISSION:New("Suppression") -end -self:_CreateMenuGroup() -end -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) -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 -function SUPPRESSION:onafterStatus(Controllable,From,Event,To) -local group=self.Controllable -if group then -local nunits=group:CountAliveUnits() -if nunits>0 then -local nammo=group:GetAmmunition() -if nammo==0 then -self:OutOfAmmo() -end -self:StatusReport(false) -if self:GetState()~="Stopped"then -self:__Status(-30) -end -else -self:Stop() -end -else -self:Stop() -end -end -function SUPPRESSION:onafterHit(Controllable,From,Event,To,Unit,AttackUnit) -self:_EventFromTo("onafterHit",Event,From,To) -if From=="CombatReady"or From=="Suppressed"then -self:_Suppress() -end -local life_min,life_max,life_ave,life_ave0,groupstrength=self:_GetLife() -local Damage=100-life_ave0 -local RetreatCondition=Damage>=self.RetreatDamage-0.01 and self.RetreatZone -local Pflee=(self.PmaxFlee-self.PminFlee)/self.RetreatDamage*math.min(Damage,self.RetreatDamage)+self.PminFlee -local P=math.random(0,100) -local FleeCondition=P Prand ==> Flee)\n",Controllable:GetName(),Pflee,P) -self:T(self.lid..text) -if Damage>=99.9 then -return -end -if RetreatCondition then -self:Retreat() -elseif FleeCondition then -if self.FallbackON and AttackUnit:IsGround()then -self:FallBack(AttackUnit) -elseif self.TakecoverON then -local Hideout=self.hideout -if self.hideout==nil then -Hideout=self:_SearchHideout() -end -self:TakeCover(Hideout) -end -end -end -function SUPPRESSION:onbeforeRecovered(Controllable,From,Event,To) -self:_EventFromTo("onbeforeRecovered",Event,From,To) -local Tnow=timer.getTime() -self:T(self.lid..string.format("onbeforeRecovered: Time now: %d - Time over: %d",Tnow,self.TsuppressionOver)) -if Tnow>=self.TsuppressionOver then -return true -else -return false -end -end -function SUPPRESSION:onafterRecovered(Controllable,From,Event,To) -self:_EventFromTo("onafterRecovered",Event,From,To) -if Controllable and Controllable:IsAlive()then -local text=string.format("Group %s has recovered!",Controllable:GetName()) -MESSAGE:New(text,10):ToAllIf(self.Debug) -self:T(self.lid..text) -self:_SetROE() -if self.flare or self.Debug then -Controllable:FlareGreen() -end -end -end -function SUPPRESSION:onafterFightBack(Controllable,From,Event,To) -self:_EventFromTo("onafterFightBack",Event,From,To) -self:_SetROE() -self:_SetAlarmState() -end -function SUPPRESSION:onbeforeFallBack(Controllable,From,Event,To,AttackUnit) -self:_EventFromTo("onbeforeFallBack",Event,From,To) -if From=="FallingBack"then -return false -else -return true -end -end -function SUPPRESSION:onafterFallBack(Controllable,From,Event,To,AttackUnit) -self:_EventFromTo("onafterFallback",Event,From,To) -self:T(self.lid..string.format("Group %s is falling back after %d hits.",Controllable:GetName(),self.Nhit)) -local ACoord=AttackUnit:GetCoordinate() -local DCoord=Controllable:GetCoordinate() -local heading=self:_Heading(ACoord,DCoord) -if self.FallbackHeading then -heading=self.FallbackHeading -end -local Coord=DCoord:Translate(self.FallbackDist,heading) -if self.Debug then -local MarkerID=Coord:MarkToAll("Fall back position for group "..Controllable:GetName()) -end -if self.smoke or self.Debug then -Coord:SmokeBlue() -end -self:_SetROE(SUPPRESSION.ROE.Hold) -self:_SetAlarmState(SUPPRESSION.AlarmState.Green) -self:_Run(Coord,self.Speed,self.Formation,self.FallbackWait) -end -function SUPPRESSION:onbeforeTakeCover(Controllable,From,Event,To,Hideout) -self:_EventFromTo("onbeforeTakeCover",Event,From,To) -if From=="TakingCover"then -return false -end -if Hideout~=nil then -return true -else -return false -end -end -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 -if self.smoke or self.Debug then -Hideout:SmokeBlue() -end -self:_SetROE(SUPPRESSION.ROE.Hold) -self:_SetAlarmState(SUPPRESSION.AlarmState.Green) -self:_Run(Hideout,self.Speed,self.Formation,self.TakecoverWait) -end -function SUPPRESSION:onafterOutOfAmmo(Controllable,From,Event,To) -self:_EventFromTo("onafterOutOfAmmo",Event,From,To) -self:I(self.lid..string.format("Out of ammo!")) -if self.RetreatZone then -self:Retreat() -end -end -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 -function SUPPRESSION:onafterRetreat(Controllable,From,Event,To) -self:_EventFromTo("onafterRetreat",Event,From,To) -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) -local ZoneCoord=self.RetreatZone:GetRandomCoordinate() -local ZoneVec2=ZoneCoord:GetVec2() -if self.smoke or self.Debug then -ZoneCoord:SmokeBlue() -end -if self.Debug then -self.RetreatZone:SmokeZone(SMOKECOLOR.Red,12) -end -self:_SetROE(SUPPRESSION.ROE.Hold) -self:_SetAlarmState(SUPPRESSION.AlarmState.Green) -self:_Run(ZoneCoord,self.Speed,self.Formation,self.RetreatWait) -end -function SUPPRESSION:onbeforeRetreated(Controllable,From,Event,To) -self:_EventFromTo("onbeforeRetreated",Event,From,To) -local inzone=self.RetreatZone:IsVec3InZone(Controllable:GetVec3()) -return inzone -end -function SUPPRESSION:onafterRetreated(Controllable,From,Event,To) -self:_EventFromTo("onafterRetreated",Event,From,To) -self:_SetROE(SUPPRESSION.ROE.Return) -self:_SetAlarmState(SUPPRESSION.AlarmState.Auto) -end -function SUPPRESSION:onafterDead(Controllable,From,Event,To) -self:_EventFromTo("onafterDead",Event,From,To) -local group=self.Controllable -if group then -local nunits=group:CountAliveUnits() -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) -if nunits==0 then -self:Stop() -end -else -self:Stop() -end -end -function SUPPRESSION:onafterStop(Controllable,From,Event,To) -self:_EventFromTo("onafterStop",Event,From,To) -local text=string.format("Stopping SUPPRESSION for group %s",self.Controllable:GetName()) -MESSAGE:New(text,10):ToAllIf(self.Debug) -self:I(self.lid..text) -self.CallScheduler:Clear() -if self.mooseevents then -self:UnHandleEvent(EVENTS.Dead) -self:UnHandleEvent(EVENTS.Hit) -else -world.removeEventHandler(self) -end -end -function SUPPRESSION:onEvent(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 -if Event.id==world.event.S_EVENT_HIT then -self:_OnEventHit(EventData) -end -if Event.id==world.event.S_EVENT_DEAD then -self:_OnEventDead(EventData) -end -end -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 -if GroupNameTgt==GroupNameSelf then -self:T(self.lid..string.format("Hit event at t = %5.1f",timer.getTime())) -if self.flare or self.Debug then -TgtUnit:FlareRed() -end -self.Nhit=self.Nhit+1 -self:T(self.lid..string.format("Group %s has just been hit %d times.",self.Controllable:GetName(),self.Nhit)) -local life=tgt:getLife()/(tgt:getLife0()+1)*100 -self:T2(self.lid..string.format("Target unit life = %5.1f",life)) -self:__Hit(3,TgtUnit,IniUnit) -end -end -function SUPPRESSION:_OnEventDead(EventData) -local GroupNameSelf=self.Controllable:GetName() -local GroupNameIni=EventData.IniGroupName -if GroupNameIni==GroupNameSelf then -local IniUnit=EventData.IniUnit -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 -if IniUnit and(self.flare or self.Debug)then -IniUnit:FlareWhite() -self:T(self.lid..string.format("Flare Dead MOOSE unit.")) -end -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 -self:Status() -self:__Dead(0.1) -end -end -function SUPPRESSION:_Suppress() -local Tnow=timer.getTime() -local Controllable=self.Controllable -self:_SetROE(SUPPRESSION.ROE.Hold) -local sigma=(self.Tsuppress_max-self.Tsuppress_min)/4 -local Tsuppress=self:_Random_Gaussian(self.Tsuppress_ave,sigma,self.Tsuppress_min,self.Tsuppress_max) -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 -if renew then -self:__Recovered(self.TsuppressionOver-Tnow) -end -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 -function SUPPRESSION:_Run(fin,speed,formation,wait) -speed=speed or 20 -formation=formation or"Off road" -wait=wait or 30 -local group=self.Controllable -if group and group:IsAlive()then -group:ClearTasks() -local ini=group:GetCoordinate() -local dist=ini:Get2DDistance(fin) -local heading=self:_Heading(ini,fin) -local nx -if dist<=50 then -nx=2 -elseif dist<=100 then -nx=3 -elseif dist<=500 then -nx=4 -else -nx=5 -end -local dx=dist/(nx-1) -local wp={} -local tasks={} -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)) -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 -local ConditionWait=group:TaskCondition(nil,nil,nil,nil,wait,nil) -local TaskHold=group:TaskHold() -local TaskComboFin={} -TaskComboFin[#TaskComboFin+1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint",self,#wp,true) -TaskComboFin[#TaskComboFin+1]=group:TaskControlled(TaskHold,ConditionWait) -tasks[#tasks+1]=group:TaskCombo(TaskComboFin) -local Waypoints=group:GetTemplateRoutePoints() -for i,p in ipairs(wp)do -table.insert(Waypoints,i,wp[i]) -end -for i,wp in ipairs(Waypoints)do -group:SetTaskWaypoint(Waypoints[i],tasks[i]) -end -group:Route(Waypoints) -else -self:E(self.lid..string.format("ERROR: Group is not alive!")) -end -end -function SUPPRESSION._Passing_Waypoint(group,Fsm,i,final) -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 -Fsm:Retreated() -else -Fsm:FightBack() -end -end -end -function SUPPRESSION:_SearchHideout() -local Zone=ZONE_GROUP:New("Zone_Hiding",self.Controllable,self.TakecoverRange) -local gpos=self.Controllable:GetCoordinate() -Zone:Scan(Object.Category.SCENERY) -local hideouts={} -for SceneryTypeName,SceneryData in pairs(Zone:GetScannedScenery())do -for SceneryName,SceneryObject in pairs(SceneryData)do -local SceneryObject=SceneryObject -local spos=SceneryObject:GetCoordinate() -local distance=spos:Get2DDistance(gpos) -if self.Debug then -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 -table.insert(hideouts,{object=SceneryObject,distance=distance}) -end -end -local Hideout=nil -if#hideouts>0 then -self:T(self.lid.."Number of hideouts "..#hideouts) -local _sort=function(a,b)return a.distancelife_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 n==0 then -return 0,0,0,0,0 -end -life_ave0=life_ave/self.IniGroupStrength -life_ave=life_ave/n -return life_min,life_max,life_ave,life_ave0,groupstrength -else -return 0,0,0,0,0 -end -end -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 -function SUPPRESSION:_Random_Gaussian(x0,sigma,xmin,xmax) -sigma=sigma or 5 -local r -local gotit=false -local i=0 -while not gotit do -local x1=math.random() -local x2=math.random() -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 -function SUPPRESSION:_SetROE(roe) -local group=self.Controllable -roe=roe or self.DefaultROE -self.CurrentROE=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 -function SUPPRESSION:_SetAlarmState(state) -local group=self.Controllable -state=state or self.DefaultAlarmState -self.CurrentAlarmState=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 -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 -PSEUDOATC={ -ClassName="PSEUDOATC", -group={}, -Debug=false, -mdur=30, -mrefresh=120, -talt=3, -chatty=true, -eventsmoose=true, -} -PSEUDOATC.id="PseudoATC | " -PSEUDOATC.version="0.9.2" -function PSEUDOATC:New() -local self=BASE:Inherit(self,BASE:New()) -self:E(PSEUDOATC.id..string.format("PseudoATC version %s",PSEUDOATC.version)) -return self -end -function PSEUDOATC:Start() -self:F() -self:E(PSEUDOATC.id.."Starting PseudoATC") -if self.eventsmoose then -self:T(PSEUDOATC.id.."Events are handled by MOOSE.") -self:HandleEvent(EVENTS.Birth,self._OnBirth) -self:HandleEvent(EVENTS.Land,self._PlayerLanded) -self:HandleEvent(EVENTS.Takeoff,self._PlayerTakeOff) -self:HandleEvent(EVENTS.PlayerLeaveUnit,self._PlayerLeft) -self:HandleEvent(EVENTS.Crash,self._PlayerLeft) -else -self:T(PSEUDOATC.id.."Events are handled by DCS.") -world.addEventHandler(self) -end -end -function PSEUDOATC:DebugOn() -self.Debug=true -end -function PSEUDOATC:DebugOff() -self.Debug=false -end -function PSEUDOATC:ChattyOn() -self.chatty=true -end -function PSEUDOATC:ChattyOff() -self.chatty=false -end -function PSEUDOATC:SetMessageDuration(duration) -self.mdur=duration or 30 -end -function PSEUDOATC:SetMenuRefresh(interval) -self.mrefresh=interval or 120 -end -function PSEUDOATC:SetEventsMoose(switch) -self.eventsmoose=switch -end -function PSEUDOATC:SetReportAltInterval(interval) -self.talt=interval or 3 -end -function PSEUDOATC:onEvent(Event) -if Event==nil or Event.initiator==nil or Unit.getByName(Event.initiator:getName())==nil then -return true -end -local DCSiniunit=Event.initiator -local DCSplace=Event.place -local DCSsubplace=Event.subplace -local EventData={} -local _playerunit=nil -local _playername=nil -if Event.initiator then -EventData.IniUnitName=Event.initiator:getName() -EventData.IniDCSGroup=Event.initiator:getGroup() -EventData.IniGroupName=Event.initiator:getGroup():getName() -_playerunit,_playername=self:_GetPlayerUnitAndName(EventData.IniUnitName) -end -if Event.place then -EventData.Place=Event.place -EventData.PlaceName=Event.place:getName() -end -if Event.subplace then -EventData.SubPlace=Event.subplace -EventData.SubPlaceName=Event.subplace:getName() -end -self:T3(PSEUDOATC.id..string.format("EVENT: Event in onEvent with ID = %s",tostring(Event.id))) -self:T3(PSEUDOATC.id..string.format("EVENT: Ini unit = %s",tostring(EventData.IniUnitName))) -self:T3(PSEUDOATC.id..string.format("EVENT: Ini group = %s",tostring(EventData.IniGroupName))) -self:T3(PSEUDOATC.id..string.format("EVENT: Ini player = %s",tostring(_playername))) -self:T3(PSEUDOATC.id..string.format("EVENT: Place = %s",tostring(EventData.PlaceName))) -self:T3(PSEUDOATC.id..string.format("EVENT: SubPlace = %s",tostring(EventData.SubPlaceName))) -if Event.id==world.event.S_EVENT_BIRTH and _playername then -self:_OnBirth(EventData) -end -if Event.id==world.event.S_EVENT_TAKEOFF and _playername and EventData.Place then -self:_PlayerTakeOff(EventData) -end -if Event.id==world.event.S_EVENT_LAND and _playername and EventData.Place then -self:_PlayerLanded(EventData) -end -if Event.id==world.event.S_EVENT_PLAYER_LEAVE_UNIT and _playername then -self:_PlayerLeft(EventData) -end -if Event.id==world.event.S_EVENT_CRASH and _playername then -self:_PlayerLeft(EventData) -end -end -function PSEUDOATC:_OnBirth(EventData) -self:F({EventData=EventData}) -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -self:PlayerEntered(_unit) -end -end -function PSEUDOATC:_PlayerLeft(EventData) -self:F({EventData=EventData}) -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -self:PlayerLeft(_unit) -end -end -function PSEUDOATC:_PlayerLanded(EventData) -self:F({EventData=EventData}) -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -local _base=nil -local _baseName=nil -if EventData.place then -_base=EventData.place -_baseName=EventData.place:getName() -end -if _unit and _playername and _base then -self:PlayerLanded(_unit,_baseName) -end -end -function PSEUDOATC:_PlayerTakeOff(EventData) -self:F({EventData=EventData}) -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -local _base=nil -local _baseName=nil -if EventData.place then -_base=EventData.place -_baseName=EventData.place:getName() -end -if _unit and _playername and _base then -self:PlayerTakeOff(_unit,_baseName) -end -end -function PSEUDOATC:PlayerEntered(unit) -self:F2({unit=unit}) -local group=unit:GetGroup() -local GID=group:GetID() -local GroupName=group:GetName() -local PlayerName=unit:GetPlayerName() -local UnitName=unit:GetName() -local CallSign=unit:GetCallsign() -local UID=unit:GetDCSObject():getID() -if not self.group[GID]then -self.group[GID]={} -self.group[GID].player={} -end -self.group[GID].player[UID]={} -self.group[GID].player[UID].group=group -self.group[GID].player[UID].unit=unit -self.group[GID].player[UID].groupname=GroupName -self.group[GID].player[UID].unitname=UnitName -self.group[GID].player[UID].playername=PlayerName -self.group[GID].player[UID].callsign=CallSign -self.group[GID].player[UID].waypoints=group:GetTaskRoute() -local text=string.format("Player %s entered unit %s of group %s (id=%d).",PlayerName,UnitName,GroupName,GID) -self:T(PSEUDOATC.id..text) -MESSAGE:New(text,30):ToAllIf(self.Debug) -local countPlayerInGroup=0 -for _ in pairs(self.group[GID].player)do countPlayerInGroup=countPlayerInGroup+1 end -if countPlayerInGroup<=1 then -self.group[GID].menu_main=missionCommands.addSubMenuForGroup(GID,"Pseudo ATC") -end -self:MenuCreatePlayer(GID,UID) -self:LocalAirports(GID,UID) -self:MenuAirports(GID,UID) -self:MenuWaypoints(GID,UID) -self.group[GID].player[UID].scheduler,self.group[GID].player[UID].schedulerid=SCHEDULER:New(nil,self.MenuRefresh,{self,GID,UID},self.mrefresh,self.mrefresh) -end -function PSEUDOATC:PlayerLanded(unit,place) -self:F2({unit=unit,place=place}) -local group=unit:GetGroup() -local GID=group:GetID() -local UID=unit:GetDCSObject():getID() -local PlayerName=self.group[GID].player[UID].playername -local UnitName=self.group[GID].player[UID].unitname -local GroupName=self.group[GID].player[UID].groupname -local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.",PlayerName,UnitName,GroupName,GID,place) -self:T(PSEUDOATC.id..text) -MESSAGE:New(text,30):ToAllIf(self.Debug) -self:AltitudeTimerStop(GID,UID) -if place and self.chatty then -local text=string.format("Touchdown! Welcome to %s pilot %s. Have a nice day!",place,PlayerName) -MESSAGE:New(text,self.mdur):ToGroup(group) -end -end -function PSEUDOATC:PlayerTakeOff(unit,place) -self:F2({unit=unit,place=place}) -local group=unit:GetGroup() -local GID=group:GetID() -local UID=unit:GetDCSObject():getID() -local PlayerName=self.group[GID].player[UID].playername -local CallSign=self.group[GID].player[UID].callsign -local UnitName=self.group[GID].player[UID].unitname -local GroupName=self.group[GID].player[UID].groupname -local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.",PlayerName,UnitName,GroupName,GID,place) -self:T(PSEUDOATC.id..text) -MESSAGE:New(text,30):ToAllIf(self.Debug) -if place and self.chatty then -local text=string.format("%s, %s, you are airborne. Have a safe trip!",place,CallSign) -MESSAGE:New(text,self.mdur):ToGroup(group) -end -end -function PSEUDOATC:PlayerLeft(unit) -self:F({unit=unit}) -local group=unit:GetGroup() -local GID=group:GetID() -local UID=unit:GetDCSObject():getID() -if self.group[GID].player[UID]then -local PlayerName=self.group[GID].player[UID].playername -local CallSign=self.group[GID].player[UID].callsign -local UnitName=self.group[GID].player[UID].unitname -local GroupName=self.group[GID].player[UID].groupname -local text=string.format("Player %s (callsign %s) of group %s just left unit %s.",PlayerName,CallSign,GroupName,UnitName) -self:T(PSEUDOATC.id..text) -MESSAGE:New(text,30):ToAllIf(self.Debug) -if self.group[GID].player[UID].schedulerid then -self.group[GID].player[UID].scheduler:Stop(self.group[GID].player[UID].schedulerid) -end -self:AltitudeTimerStop(GID,UID) -if self.group[GID].player[UID].menu_own then -missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_own) -end -local countPlayerInGroup=0 -for _ in pairs(self.group[GID].player)do countPlayerInGroup=countPlayerInGroup+1 end -if self.group[GID].menu_main and countPlayerInGroup==1 then -missionCommands.removeItemForGroup(GID,self.group[GID].menu_main) -end -self.group[GID].player[UID]=nil -end -end -function PSEUDOATC:MenuRefresh(GID,UID) -self:F({GID=GID,UID=UID}) -local text=string.format("Refreshing menues for player %s in group %s.",self.group[GID].player[UID].playername,self.group[GID].player[UID].groupname) -self:T(PSEUDOATC.id..text) -MESSAGE:New(text,30):ToAllIf(self.Debug) -self:MenuClear(GID,UID) -self:LocalAirports(GID,UID) -self:MenuAirports(GID,UID) -self:MenuWaypoints(GID,UID) -end -function PSEUDOATC:MenuCreatePlayer(GID,UID) -self:F({GID=GID,UID=UID}) -local PlayerName=self.group[GID].player[UID].playername -self.group[GID].player[UID].menu_own=missionCommands.addSubMenuForGroup(GID,PlayerName,self.group[GID].menu_main) -end -function PSEUDOATC:MenuClear(GID,UID) -self:F({GID=GID,UID=UID}) -local text=string.format("Clearing menus for player %s in group %s.",self.group[GID].player[UID].playername,self.group[GID].player[UID].groupname) -self:T(PSEUDOATC.id..text) -MESSAGE:New(text,30):ToAllIf(self.Debug) -if self.group[GID].player[UID].menu_airports then -missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_airports) -self.group[GID].player[UID].menu_airports=nil -else -self:T2(PSEUDOATC.id.."No airports to clear menus.") -end -if self.group[GID].player[UID].menu_waypoints then -missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_waypoints) -self.group[GID].player[UID].menu_waypoints=nil -end -if self.group[GID].player[UID].menu_reportalt then -missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_reportalt) -self.group[GID].player[UID].menu_reportalt=nil -end -if self.group[GID].player[UID].menu_requestalt then -missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_requestalt) -self.group[GID].player[UID].menu_requestalt=nil -end -end -function PSEUDOATC:MenuAirports(GID,UID) -self:F({GID=GID,UID=UID}) -self.group[GID].player[UID].menu_airports=missionCommands.addSubMenuForGroup(GID,"Local Airports",self.group[GID].player[UID].menu_own) -local i=0 -for _,airport in pairs(self.group[GID].player[UID].airports)do -i=i+1 -if i>10 then -break -end -local name=airport.name -local d=airport.distance -local pos=AIRBASE:FindByName(name):GetCoordinate() -local submenu=missionCommands.addSubMenuForGroup(GID,name,self.group[GID].player[UID].menu_airports) -missionCommands.addCommandForGroup(GID,"Weather Report",submenu,self.ReportWeather,self,GID,UID,pos,name) -missionCommands.addCommandForGroup(GID,"Request BR",submenu,self.ReportBR,self,GID,UID,pos,name) -self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d",name,GID)) -end -end -function PSEUDOATC:MenuWaypoints(GID,UID) -self:F({GID=GID,UID=UID}) -local callsign=self.group[GID].player[UID].callsign -self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).",callsign,GID)) -if#self.group[GID].player[UID].waypoints>0 then -self.group[GID].player[UID].menu_waypoints=missionCommands.addSubMenuForGroup(GID,"Waypoints",self.group[GID].player[UID].menu_own) -local j=0 -for i,wp in pairs(self.group[GID].player[UID].waypoints)do -j=j+1 -if j>10 then -break -end -local pos=COORDINATE:New(wp.x,wp.alt,wp.y) -local name=string.format("Waypoint %d",i-1) -local submenu=missionCommands.addSubMenuForGroup(GID,name,self.group[GID].player[UID].menu_waypoints) -missionCommands.addCommandForGroup(GID,"Weather Report",submenu,self.ReportWeather,self,GID,UID,pos,name) -missionCommands.addCommandForGroup(GID,"Request BR",submenu,self.ReportBR,self,GID,UID,pos,name) -end -end -self.group[GID].player[UID].menu_reportalt=missionCommands.addCommandForGroup(GID,"Talk me down",self.group[GID].player[UID].menu_own,self.AltidudeTimerToggle,self,GID,UID) -self.group[GID].player[UID].menu_requestalt=missionCommands.addCommandForGroup(GID,"Request altitude",self.group[GID].player[UID].menu_own,self.ReportHeight,self,GID,UID) -end -function PSEUDOATC:ReportWeather(GID,UID,position,location) -self:F({GID=GID,UID=UID,position=position,location=location}) -local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername)or _SETTINGS -local text=string.format("Local weather at %s:\n",location) -local Pqnh=position:GetPressure(0) -local Pqfe=position:GetPressure() -local hPa2inHg=0.0295299830714 -local hPa2mmHg=0.7500615613030 -local _Pqnh=string.format("%.2f inHg",Pqnh*hPa2inHg) -local _Pqfe=string.format("%.2f inHg",Pqfe*hPa2inHg) -if settings:IsMetric()then -_Pqnh=string.format("%.1f mmHg",Pqnh*hPa2mmHg) -_Pqfe=string.format("%.1f mmHg",Pqfe*hPa2mmHg) -end -text=text..string.format("QFE %.1f hPa = %s.\n",Pqfe,_Pqfe) -text=text..string.format("QNH %.1f hPa = %s.\n",Pqnh,_Pqnh) -local T=position:GetTemperature() -local _T=string.format('%d°F',UTILS.CelciusToFarenheit(T)) -if settings:IsMetric()then -_T=string.format('%d°C',T) -end -local text=text..string.format("Temperature %s\n",_T) -local Dir,Vel=position:GetWind() -local Bn,Bd=UTILS.BeaufortScale(Vel) -local Ds=string.format('%03d°',Dir) -local Vs=string.format("%.1f knots",UTILS.MpsToKnots(Vel)) -if settings:IsMetric()then -Vs=string.format('%.1f m/s',Vel) -end -local text=text..string.format("%s, Wind from %s at %s (%s).",self.group[GID].player[UID].playername,Ds,Vs,Bd) -self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,text,self.mdur,true) -end -function PSEUDOATC:ReportBR(GID,UID,position,location) -self:F({GID=GID,UID=UID,position=position,location=location}) -local unit=self.group[GID].player[UID].unit -local coord=unit:GetCoordinate() -local angle=coord:HeadingTo(position) -local range=coord:Get2DDistance(position) -local Bs=string.format('%03d°',angle) -local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername)or _SETTINGS -local Rs=string.format("%.1f NM",UTILS.MetersToNM(range)) -if settings:IsMetric()then -Rs=string.format("%.1f km",range/1000) -end -local text=string.format("%s: Bearing %s, Range %s.",location,Bs,Rs) -self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,text,self.mdur,true) -end -function PSEUDOATC:ReportHeight(GID,UID,dt,_clear) -self:F({GID=GID,UID=UID,dt=dt}) -local dt=dt or self.mdur -if _clear==nil then -_clear=false -end -local function get_AGL(p) -local agl=0 -local vec2={x=p.x,y=p.z} -local ground=land.getHeight(vec2) -local agl=p.y-ground -return agl -end -local unit=self.group[GID].player[UID].unit -if unit and unit:IsAlive()then -local position=unit:GetCoordinate() -local height=get_AGL(position) -local callsign=unit:GetCallsign() -local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername)or _SETTINGS -local Hs=string.format("%d ft",UTILS.MetersToFeet(height)) -if settings:IsMetric()then -Hs=string.format("%d m",height) -end -local _text=string.format("%s, your altitude is %s AGL.",callsign,Hs) -if _clear==false then -_text=_text..string.format(" FL%03d.",position.y/30.48) -end -self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,_text,dt,_clear) -return height -end -return 0 -end -function PSEUDOATC:AltidudeTimerToggle(GID,UID) -self:F({GID=GID,UID=UID}) -if self.group[GID].player[UID].altimerid then -self:AltitudeTimerStop(GID,UID) -else -self:AltitudeTimeStart(GID,UID) -end -end -function PSEUDOATC:AltitudeTimeStart(GID,UID) -self:F({GID=GID,UID=UID}) -self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.",UID)) -self.group[GID].player[UID].altimer,self.group[GID].player[UID].altimerid=SCHEDULER:New(nil,self.ReportHeight,{self,GID,UID,0.1,true},1,3) -end -function PSEUDOATC:AltitudeTimerStop(GID,UID) -self:F({GID=GID,UID=UID}) -self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.",UID)) -if self.group[GID].player[UID].altimerid then -self.group[GID].player[UID].altimer:Stop(self.group[GID].player[UID].altimerid) -end -self.group[GID].player[UID].altimer=nil -self.group[GID].player[UID].altimerid=nil -end -function PSEUDOATC:LocalAirports(GID,UID) -self:F({GID=GID,UID=UID}) -self.group[GID].player[UID].airports=nil -self.group[GID].player[UID].airports={} -local pos=self.group[GID].player[UID].unit:GetCoordinate() -for i=0,2 do -local airports=coalition.getAirbases(i) -for _,airbase in pairs(airports)do -local name=airbase:getName() -local q=AIRBASE:FindByName(name):GetCoordinate() -local d=q:Get2DDistance(pos) -table.insert(self.group[GID].player[UID].airports,{distance=d,name=name}) -end -end -local function compare(a,b) -return a.distance0 then -local samecoalition=anycoalition or Coalition==warehouse:GetCoalition() -if samecoalition and not(warehouse:IsNotReadyYet()or warehouse:IsStopped()or warehouse:IsDestroyed())then -local nassets=warehouse:GetNumberOfAssets(Descriptor,DescriptorValue) -local enough=true -if Descriptor and DescriptorValue then -enough=nassets>=MinAssets -end -if enough and(distmin==nil or dist=1 then -local FSMstate=self:GetState() -local coalition=self:GetCoalitionName() -local country=self:GetCountryName() -self:I(self.lid..string.format("State=%s %s [%s]: Assets=%d, Requests: waiting=%d, pending=%d",FSMstate,country,coalition,#self.stock,#self.queue,#self.pending)) -end -self:_JobDone() -self:_DisplayStatus() -self:_CheckConquered() -if self:IsRunwayOperational()==false then -local Trepair=self:GetRunwayRepairtime() -self:I(self.lid..string.format("Runway destroyed! Will be repaired in %d sec",Trepair)) -if Trepair==0 then -self:RunwayRepaired() -end -end -self:_CheckRequestConsistancy(self.queue) -if self:IsRunning()or self:IsAttacked()then -local request=self:_CheckQueue() -if request then -self:Request(request) -end -end -self:_PrintQueue(self.queue,"Queue waiting") -self:_PrintQueue(self.pending,"Queue pending") -self:_CheckFuel() -self:_UpdateWarehouseMarkText() -if self.Debug then -self:_DisplayStockItems(self.stock) -end -self:__Status(-self.dTstatus) -end -function WAREHOUSE:_JobDone() -local done={} -for _,request in pairs(self.pending)do -local request=request -if request.born then -local ncargo=0 -if request.cargogroupset then -ncargo=request.cargogroupset:Count() -end -local ntransport=0 -if request.transportgroupset then -ntransport=request.transportgroupset:Count() -end -local ncargotot=request.nasset -local ncargodelivered=request.ndelivered -local ncargodead=ncargotot-ncargodelivered-ncargo -local ntransporttot=request.ntransport -local ntransporthome=request.ntransporthome -local ntransportdead=ntransporttot-ntransporthome-ntransport -local text=string.format("Request id=%d: Cargo: Ntot=%d, Nalive=%d, Ndelivered=%d, Ndead=%d | Transport: Ntot=%d, Nalive=%d, Nhome=%d, Ndead=%d", -request.uid,ncargotot,ncargo,ncargodelivered,ncargodead,ntransporttot,ntransport,ntransporthome,ntransportdead) -self:T(self.lid..text) -if ncargo==0 then -if not self.delivered[request.uid]then -self:Delivered(request) -end -if ntransport==0 then -if self.verbosity>=1 then -local text=string.format("Warehouse %s: Job on request id=%d for warehouse %s done!\n",self.alias,request.uid,request.warehouse.alias) -text=text..string.format("- %d of %d assets delivered. Casualties %d.",ncargodelivered,ncargotot,ncargodead) -if request.ntransport>0 then -text=text..string.format("\n- %d of %d transports returned home. Casualties %d.",ntransporthome,ntransporttot,ntransportdead) -end -self:_InfoMessage(text,20) -end -table.insert(done,request) -else -for _,_group in pairs(request.transportgroupset:GetSetObjects())do -local group=_group -if group and group:IsAlive()then -local category=group:GetCategory() -local speed=group:GetVelocityKMH() -local notmoving=speed<1 -local airbase=group:GetCoordinate():GetClosestAirbase():GetName() -local athomebase=self.airbase and self.airbase:GetName()==airbase -local onground=not group:InAir() -local inspawnzone=group:IsPartlyOrCompletelyInZone(self.spawnzone) -local ishome=false -if category==Group.Category.GROUND or category==Group.Category.HELICOPTER then -ishome=inspawnzone and onground and notmoving -elseif category==Group.Category.AIRPLANE then -ishome=athomebase and onground and notmoving -end -local text=string.format("Group %s: speed=%d km/h, onground=%s , airbase=%s, spawnzone=%s ==> ishome=%s",group:GetName(),speed,tostring(onground),airbase,tostring(inspawnzone),tostring(ishome)) -self:T(self.lid..text) -if ishome then -local text=string.format("Warehouse %s: Transport group arrived back home and no cargo left for request id=%d.\nSending transport group %s back to stock.",self.alias,request.uid,group:GetName()) -self:T(self.lid..text) -if self.Debug then -group:SmokeRed() -end -self:Arrived(group) -end -end -end -end -else -if ntransport==0 and request.ntransport>0 then -local ncargoalive=0 -for _,_group in pairs(request.cargogroupset:GetSetObjects())do -local groupname=_group:GetName() -local group=GROUP:FindByName(groupname.."#CARGO") -if group and group:IsAlive()then -if group:IsPartlyOrCompletelyInZone(self.spawnzone)then -if self.Debug then -group:SmokeBlue() -end -self:AddAsset(group) -ncargoalive=ncargoalive+1 -end -end -end -self:_InfoMessage(string.format("Warehouse %s: All transports of request id=%s dead! Putting remaining %s cargo assets back into warehouse!",self.alias,request.uid,ncargoalive)) -end -end -end -end -for _,request in pairs(done)do -self:_DeleteQueueItem(request,self.pending) -end -end -function WAREHOUSE:_CheckAssetStatus() -local function _CheckGroup(_request,_group) -local request=_request -local group=_group -if group and group:IsAlive()then -local category=group:GetCategory() -for _,_unit in pairs(group:GetUnits())do -local unit=_unit -if unit and unit:IsAlive()then -local unitid=unit:GetID() -local life9=unit:GetLife() -local life0=unit:GetLife0() -local life=life9/life0*100 -local speed=unit:GetVelocityMPS() -local onground=unit:InAir() -local problem=false -if life<10 then -self:T(string.format("Unit %s is heavily damaged!",unit:GetName())) -end -if speed<1 and unit:GetSpeedMax()>1 and onground then -self:T(string.format("Unit %s is not moving!",unit:GetName())) -problem=true -end -if problem then -if request.assetproblem[unitid]then -local deltaT=timer.getAbsTime()-request.assetproblem[unitid] -if deltaT>300 then -unit:Destroy() -end -else -request.assetproblem[unitid]=timer.getAbsTime() -end -end -end -end -end -end -for _,request in pairs(self.pending)do -local request=request -if request.cargogroupset then -for _,_group in pairs(request.cargogroupset:GetSet())do -local group=_group -_CheckGroup(request,group) -end -end -if request.transportgroupset then -for _,group in pairs(request.transportgroupset:GetSet())do -_CheckGroup(request,group) -end -end -end -end -function WAREHOUSE:onafterAddAsset(From,Event,To,group,ngroups,forceattribute,forcecargobay,forceweight,loadradius,skill,liveries,assignment,other) -self:T({group=group,ngroups=ngroups,forceattribute=forceattribute,forcecargobay=forcecargobay,forceweight=forceweight}) -local n=ngroups or 1 -if type(group)=="string"then -group=GROUP:FindByName(group) -end -if liveries and type(liveries)=="string"then -liveries={liveries} -end -if group then -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid and aid and rid then -local warehouse=self:FindWarehouseInDB(wid) -if warehouse then -local request=warehouse:_GetRequestOfGroup(group,warehouse.pending) -if request then -local istransport=warehouse:_GroupIsTransport(group,request) -if istransport==true then -request.ntransporthome=request.ntransporthome+1 -request.transportgroupset:Remove(group:GetName(),true) -local ntrans=request.transportgroupset:Count() -self:T2(warehouse.lid..string.format("Transport %d of %s returned home. TransportSet=%d",request.ntransporthome,tostring(request.ntransport),ntrans)) -elseif istransport==false then -request.ndelivered=request.ndelivered+1 -local namewo=self:_GetNameWithOut(group) -request.cargogroupset:Remove(namewo,true) -local ncargo=request.cargogroupset:Count() -self:T2(warehouse.lid..string.format("Cargo %s: %d of %s delivered. CargoSet=%d",namewo,request.ndelivered,tostring(request.nasset),ncargo)) -else -self:E(warehouse.lid..string.format("WARNING: Group %s is neither cargo nor transport! Need to investigate...",group:GetName())) -end -if assignment==nil and request.assignment~=nil then -assignment=request.assignment -end -end -end -local asset=self:FindAssetInDB(group) -if asset~=nil then -self:_DebugMessage(string.format("Warehouse %s: Adding KNOWN asset uid=%d with attribute=%s to stock.",self.alias,asset.uid,asset.attribute),5) -if liveries then -if type(liveries)=="table"then -asset.livery=liveries[math.random(#liveries)] -else -asset.livery=liveries -end -end -asset.skill=skill or asset.skill -asset.wid=self.uid -asset.rid=nil -asset.spawned=false -asset.iscargo=nil -asset.arrived=nil -if group:IsAlive()==true then -asset.damage=asset.life0-group:GetLife() -end -table.insert(self.stock,asset) -self:__NewAsset(0.1,asset,assignment or"") -else -self:_ErrorMessage(string.format("ERROR: Known asset could not be found in global warehouse db!"),0) -end -else -self:_DebugMessage(string.format("Warehouse %s: Adding %d NEW assets of group %s to stock",self.alias,n,tostring(group:GetName())),5) -local assets=self:_RegisterAsset(group,n,forceattribute,forcecargobay,forceweight,loadradius,liveries,skill,assignment) -for _,asset in pairs(assets)do -asset.wid=self.uid -asset.rid=nil -table.insert(self.stock,asset) -self:__NewAsset(0.1,asset,assignment or"") -end -end -if group:IsAlive()==true then -self:_DebugMessage(string.format("Removing group %s",group:GetName()),5) -group:Destroy() -end -else -self:E(self.lid.."ERROR: Unknown group added as asset!") -self:E({unknowngroup=group}) -end -end -function WAREHOUSE:_RegisterAsset(group,ngroups,forceattribute,forcecargobay,forceweight,loadradius,liveries,skill,assignment) -self:F({groupname=group:GetName(),ngroups=ngroups,forceattribute=forceattribute,forcecargobay=forcecargobay,forceweight=forceweight}) -local n=ngroups or 1 -local function _GetObjectSize(DCSdesc) -if DCSdesc.box then -local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) -local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) -local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) -return math.max(x,z),x,y,z -end -return 0,0,0,0 -end -local templategroupname=group:GetName() -local Descriptors=group:GetUnit(1):GetDesc() -local Category=group:GetCategory() -local TypeName=group:GetTypeName() -local SpeedMax=group:GetSpeedMax() -local RangeMin=group:GetRange() -local smax,sx,sy,sz=_GetObjectSize(Descriptors) -local weight=0 -local cargobay={} -local cargobaytot=0 -local cargobaymax=0 -for _i,_unit in pairs(group:GetUnits())do -local unit=_unit -local Desc=unit:GetDesc() -local unitweight=forceweight or Desc.massEmpty -if unitweight then -weight=weight+unitweight -end -local cargomax=0 -local massfuel=Desc.fuelMassMax or 0 -local massempty=Desc.massEmpty or 0 -local massmax=Desc.massMax or 0 -cargomax=massmax-massfuel-massempty -self:T3(self.lid..string.format("Unit name=%s: mass empty=%.1f kg, fuel=%.1f kg, max=%.1f kg ==> cargo=%.1f kg",unit:GetName(),unitweight,massfuel,massmax,cargomax)) -local bay=forcecargobay or unit:GetCargoBayFreeWeight() -table.insert(cargobay,bay) -cargobaytot=cargobaytot+bay -if bay>cargobaymax then -cargobaymax=bay -end -end -local attribute=forceattribute or self:_GetAttribute(group) -local assets={} -for i=1,n do -local asset={} -_WAREHOUSEDB.AssetID=_WAREHOUSEDB.AssetID+1 -asset.uid=_WAREHOUSEDB.AssetID -asset.templatename=templategroupname -asset.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) -asset.category=Category -asset.unittype=TypeName -asset.nunits=#asset.template.units -asset.range=RangeMin -asset.speedmax=SpeedMax -asset.size=smax -asset.weight=weight -asset.DCSdesc=Descriptors -asset.attribute=attribute -asset.cargobay=cargobay -asset.cargobaytot=cargobaytot -asset.cargobaymax=cargobaymax -asset.loadradius=loadradius -if liveries then -asset.livery=liveries[math.random(#liveries)] -end -asset.skill=skill -asset.assignment=assignment -asset.spawned=false -asset.life0=group:GetLife0() -asset.damage=0 -asset.spawngroupname=string.format("%s_AID-%d",templategroupname,asset.uid) -if i==1 then -self:_AssetItemInfo(asset) -end -_WAREHOUSEDB.Assets[asset.uid]=asset -table.insert(assets,asset) -end -return assets -end -function WAREHOUSE:_AssetItemInfo(asset) -local text=string.format("\nNew asset with id=%d for warehouse %s:\n",asset.uid,self.alias) -text=text..string.format("Spawngroup name= %s\n",asset.spawngroupname) -text=text..string.format("Template name = %s\n",asset.templatename) -text=text..string.format("Unit type = %s\n",asset.unittype) -text=text..string.format("Attribute = %s\n",asset.attribute) -text=text..string.format("Category = %d\n",asset.category) -text=text..string.format("Units # = %d\n",asset.nunits) -text=text..string.format("Speed max = %5.2f km/h\n",asset.speedmax) -text=text..string.format("Range max = %5.2f km\n",asset.range/1000) -text=text..string.format("Size max = %5.2f m\n",asset.size) -text=text..string.format("Weight total = %5.2f kg\n",asset.weight) -text=text..string.format("Cargo bay tot = %5.2f kg\n",asset.cargobaytot) -text=text..string.format("Cargo bay max = %5.2f kg\n",asset.cargobaymax) -text=text..string.format("Load radius = %s m\n",tostring(asset.loadradius)) -text=text..string.format("Skill = %s\n",tostring(asset.skill)) -text=text..string.format("Livery = %s",tostring(asset.livery)) -self:I(self.lid..text) -self:T({DCSdesc=asset.DCSdesc}) -self:T3({Template=asset.template}) -end -function WAREHOUSE:onafterNewAsset(From,Event,To,asset,assignment) -self:T(self.lid..string.format("New asset %s id=%d with assignment %s.",tostring(asset.templatename),asset.uid,tostring(assignment))) -end -function WAREHOUSE:onbeforeAddRequest(From,Event,To,warehouse,AssetDescriptor,AssetDescriptorValue,nAsset,TransportType,nTransport,Assignment,Prio) -local okay=true -if AssetDescriptor==WAREHOUSE.Descriptor.ATTRIBUTE then -local gotit=false -for _,attribute in pairs(WAREHOUSE.Attribute)do -if AssetDescriptorValue==attribute then -gotit=true -end -end -if not gotit then -self:_ErrorMessage("ERROR: Invalid request. Asset attribute is unknown!",5) -okay=false -end -elseif AssetDescriptor==WAREHOUSE.Descriptor.CATEGORY then -local gotit=false -for _,category in pairs(Group.Category)do -if AssetDescriptorValue==category then -gotit=true -end -end -if not gotit then -self:_ErrorMessage("ERROR: Invalid request. Asset category is unknown!",5) -okay=false -end -elseif AssetDescriptor==WAREHOUSE.Descriptor.GROUPNAME then -if type(AssetDescriptorValue)~="string"then -self:_ErrorMessage("ERROR: Invalid request. Asset template name must be passed as a string!",5) -okay=false -end -elseif AssetDescriptor==WAREHOUSE.Descriptor.UNITTYPE then -if type(AssetDescriptorValue)~="string"then -self:_ErrorMessage("ERROR: Invalid request. Asset unit type must be passed as a string!",5) -okay=false -end -elseif AssetDescriptor==WAREHOUSE.Descriptor.ASSIGNMENT then -if type(AssetDescriptorValue)~="string"then -self:_ErrorMessage("ERROR: Invalid request. Asset assignment type must be passed as a string!",5) -okay=false -end -elseif AssetDescriptor==WAREHOUSE.Descriptor.ASSETLIST then -if type(AssetDescriptorValue)~="table"then -self:_ErrorMessage("ERROR: Invalid request. Asset assignment type must be passed as a table!",5) -okay=false -end -else -self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, GROUPNAME, UNITTYPE or ASSIGNMENT!",5) -okay=false -end -if self:IsStopped()then -self:_ErrorMessage("ERROR: Invalid request. Warehouse is stopped!",0) -okay=false -end -if self:IsDestroyed()and not self.respawnafterdestroyed then -self:_ErrorMessage("ERROR: Invalid request. Warehouse is destroyed!",0) -okay=false -end -return okay -end -function WAREHOUSE:onafterAddRequest(From,Event,To,warehouse,AssetDescriptor,AssetDescriptorValue,nAsset,TransportType,nTransport,Prio,Assignment) -nAsset=nAsset or 1 -TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED -Prio=Prio or 50 -if nTransport==nil then -if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then -nTransport=0 -else -nTransport=1 -end -end -local toself=false -if self.warehouse:GetName()==warehouse.warehouse:GetName()then -toself=true -end -self.queueid=self.queueid+1 -local request={ -uid=self.queueid, -prio=Prio, -warehouse=warehouse, -assetdesc=AssetDescriptor, -assetdescval=AssetDescriptorValue, -nasset=nAsset, -transporttype=TransportType, -ntransport=nTransport, -assignment=tostring(Assignment), -airbase=warehouse:GetAirbase(), -category=warehouse:GetAirbaseCategory(), -ndelivered=0, -ntransporthome=0, -assets={}, -toself=toself, -} -table.insert(self.queue,request) -local text=string.format("Warehouse %s: New request from warehouse %s.\nDescriptor %s=%s, #assets=%s; Transport=%s, #transports =%s.", -self.alias,warehouse.alias,request.assetdesc,tostring(request.assetdescval),tostring(request.nasset),request.transporttype,tostring(request.ntransport)) -self:_DebugMessage(text,5) -end -function WAREHOUSE:onbeforeRequest(From,Event,To,Request) -self:T3({warehouse=self.alias,request=Request}) -local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate()) -local _assets=Request.cargoassets -if Request.nasset==0 then -local text=string.format("Warehouse %s: Request denied! Zero assets were requested.",self.alias) -self:_InfoMessage(text,10) -return false -end -for _,_asset in pairs(_assets)do -local asset=_asset -if asset.range=1 then -local text=string.format("Warehouse %s: Processing request id=%d from warehouse %s.\n",self.alias,Request.uid,Request.warehouse.alias) -text=text..string.format("Requested %s assets of %s=%s.\n",tostring(Request.nasset),Request.assetdesc,Request.assetdesc==WAREHOUSE.Descriptor.ASSETLIST and"Asset list"or Request.assetdescval) -text=text..string.format("Transports %s of type %s.",tostring(Request.ntransport),tostring(Request.transporttype)) -self:_InfoMessage(text,5) -end -Request.timestamp=timer.getAbsTime() -self:_SpawnAssetRequest(Request) -local _assetstock=Request.transportassets -local Parking={} -if Request.transportcategory==Group.Category.AIRPLANE or Request.transportcategory==Group.Category.HELICOPTER then -Parking=self:_FindParkingForAssets(self.airbase,_assetstock) -end -local _transportassets={} -for i=1,Request.ntransport do -local _assetitem=_assetstock[i] -local _alias=_assetitem.spawngroupname -_assetitem.rid=Request.uid -_assetitem.spawned=false -_assetitem.iscargo=false -_assetitem.arrived=false -local spawngroup=nil -Request.assets[_assetitem.uid]=_assetitem -if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then -spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem,Request,Parking[_assetitem.uid],true) -elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then -spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem,Request,Parking[_assetitem.uid],false) -elseif Request.transporttype==WAREHOUSE.TransportType.APC then -spawngroup=self:_SpawnAssetGroundNaval(_alias,_assetitem,Request,self.spawnzone) -elseif Request.transporttype==WAREHOUSE.TransportType.TRAIN then -self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!") -return -elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER then -spawngroup=self:_SpawnAssetGroundNaval(_alias,_assetitem,Request,self.portzone) -elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then -self:_ErrorMessage("ERROR: Transport type selfpropelled was already handled above. We should not get here!") -return -else -self:_ErrorMessage("ERROR: Unknown transport type!") -return -end -end -Request.assetproblem={} -table.insert(self.pending,Request) -self:_DeleteQueueItem(Request,self.queue) -end -function WAREHOUSE:onafterRequestSpawned(From,Event,To,Request,CargoGroupSet,TransportGroupSet) -local _cargotype=Request.cargoattribute -local _cargocategory=Request.cargocategory -if Request.toself then -self:_DebugMessage(string.format("Selfrequest! Current status %s",self:GetState())) -self:__SelfRequest(1,CargoGroupSet,Request) -return -end -if Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then -self:T2(self.lid..string.format("Got selfpropelled request for %d assets.",CargoGroupSet:Count())) -for _,_group in pairs(CargoGroupSet:GetSetObjects())do -local group=_group -if _cargocategory==Group.Category.GROUND then -self:T2(self.lid..string.format("Route ground group %s.",group:GetName())) -local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate() -if self.Debug then -ToCoordinate:MarkToAll(string.format("Destination of group %s",group:GetName())) -end -self:_RouteGround(group,Request) -elseif _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then -self:T2(self.lid..string.format("Route airborne group %s.",group:GetName())) -self:_RouteAir(group) -elseif _cargocategory==Group.Category.SHIP then -self:T2(self.lid..string.format("Route naval group %s.",group:GetName())) -self:_RouteNaval(group,Request) -elseif _cargocategory==Group.Category.TRAIN then -self:T2(self.lid..string.format("Route train group %s.",group:GetName())) -self:_RouteTrain(group,Request.warehouse.rail) -else -self:E(self.lid..string.format("ERROR: unknown category %s for self propelled cargo %s!",tostring(_cargocategory),tostring(group:GetName()))) -end -end -Request.transportgroupset=TransportGroupSet -return -end -local _boardradius=500 -if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then -_boardradius=5000 -elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then -elseif Request.transporttype==WAREHOUSE.TransportType.APC then -elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER -or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then -_boardradius=6000 -end -local CargoGroups=SET_CARGO:New() -for _,_group in pairs(CargoGroupSet:GetSetObjects())do -local asset=self:FindAssetInDB(_group) -local cargogroup=CARGO_GROUP:New(_group,_cargotype,_group:GetName(),_boardradius,asset.loadradius) -cargogroup:SetWeight(asset.weight) -CargoGroups:AddCargo(cargogroup) -end -local CargoTransport -if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then -local PickupAirbaseSet=SET_ZONE:New():AddZone(ZONE_AIRBASE:New(self.airbase:GetName())) -local DeployAirbaseSet=SET_ZONE:New():AddZone(ZONE_AIRBASE:New(Request.airbase:GetName())) -CargoTransport=AI_CARGO_DISPATCHER_AIRPLANE:New(TransportGroupSet,CargoGroups,PickupAirbaseSet,DeployAirbaseSet) -CargoTransport:SetHomeZone(ZONE_AIRBASE:New(self.airbase:GetName())) -elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then -local PickupZoneSet=SET_ZONE:New():AddZone(self.spawnzone) -local DeployZoneSet=SET_ZONE:New():AddZone(Request.warehouse.spawnzone) -CargoTransport=AI_CARGO_DISPATCHER_HELICOPTER:New(TransportGroupSet,CargoGroups,PickupZoneSet,DeployZoneSet) -CargoTransport:SetHomeZone(self.spawnzone) -elseif Request.transporttype==WAREHOUSE.TransportType.APC then -local PickupZoneSet=SET_ZONE:New():AddZone(self.spawnzone) -local DeployZoneSet=SET_ZONE:New():AddZone(Request.warehouse.spawnzone) -CargoTransport=AI_CARGO_DISPATCHER_APC:New(TransportGroupSet,CargoGroups,PickupZoneSet,DeployZoneSet,0) -CargoTransport:SetHomeZone(self.spawnzone) -elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER -or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then -local PickupZoneSet=SET_ZONE:New():AddZone(self.portzone) -PickupZoneSet:AddZone(self.harborzone) -local DeployZoneSet=SET_ZONE:New():AddZone(Request.warehouse.harborzone) -local remotename=Request.warehouse.warehouse:GetName() -local ShippingLane=self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])] -CargoTransport=AI_CARGO_DISPATCHER_SHIP:New(TransportGroupSet,CargoGroups,PickupZoneSet,DeployZoneSet,ShippingLane) -CargoTransport:SetHomeZone(self.portzone) -else -self:E(self.lid.."ERROR: Unknown transporttype!") -end -local pickupouter=200 -local pickupinner=0 -local deployouter=200 -local deployinner=0 -if Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER -or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then -pickupouter=1000 -pickupinner=20 -deployouter=1000 -deployinner=0 -else -pickupouter=200 -pickupinner=0 -if self.spawnzone.Radius~=nil then -pickupouter=self.spawnzone.Radius -pickupinner=20 -end -deployouter=200 -deployinner=0 -if self.spawnzone.Radius~=nil then -deployouter=Request.warehouse.spawnzone.Radius -deployinner=20 -end -end -CargoTransport:SetPickupRadius(pickupouter,pickupinner) -CargoTransport:SetDeployRadius(deployouter,deployinner) -Request.carriercargo={} -for _,carriergroup in pairs(TransportGroupSet:GetSetObjects())do -local asset=self:FindAssetInDB(carriergroup) -for _i,_carrierunit in pairs(carriergroup:GetUnits())do -local carrierunit=_carrierunit -Request.carriercargo[carrierunit:GetName()]={} -local cargobay=asset.cargobay[_i] -carrierunit:SetCargoBayWeightLimit(cargobay) -self:T2(self.lid..string.format("Cargo bay weight limit of carrier unit %s: %.1f kg.",carrierunit:GetName(),carrierunit:GetCargoBayFreeWeight())) -end -end -function CargoTransport:OnAfterPickedUp(From,Event,To,Carrier,PickupZone) -local warehouse=Carrier:GetState(Carrier,"WAREHOUSE") -local text=string.format("Carrier group %s picked up at pickup zone %s.",Carrier:GetName(),PickupZone:GetName()) -warehouse:T(warehouse.lid..text) -end -function CargoTransport:OnAfterDeployed(From,Event,To,Carrier,DeployZone) -local warehouse=Carrier:GetState(Carrier,"WAREHOUSE") -end -function CargoTransport:OnAfterHome(From,Event,To,Carrier,Coordinate,Speed,Height,HomeZone) -local warehouse=Carrier:GetState(Carrier,"WAREHOUSE") -local text=string.format("Carrier group %s going home to zone %s.",Carrier:GetName(),HomeZone:GetName()) -warehouse:T(warehouse.lid..text) -end -function CargoTransport:OnAfterLoaded(From,Event,To,Carrier,Cargo,CarrierUnit,PickupZone) -local warehouse=Carrier:GetState(Carrier,"WAREHOUSE") -local text=string.format("Carrier group %s loaded cargo %s into unit %s in pickup zone %s",Carrier:GetName(),Cargo:GetName(),CarrierUnit:GetName(),PickupZone:GetName()) -warehouse:T(warehouse.lid..text) -local group=Cargo:GetObject() -local request=warehouse:_GetRequestOfGroup(group,warehouse.pending) -table.insert(request.carriercargo[CarrierUnit:GetName()],warehouse:_GetNameWithOut(Cargo:GetName())) -end -function CargoTransport:OnAfterUnloaded(From,Event,To,Carrier,Cargo,CarrierUnit,DeployZone) -local warehouse=Carrier:GetState(Carrier,"WAREHOUSE") -local group=Cargo:GetObject() -local text=string.format("Cargo group %s was unloaded from carrier unit %s.",tostring(group:GetName()),tostring(CarrierUnit:GetName())) -warehouse:T(warehouse.lid..text) -warehouse:Arrived(group) -end -function CargoTransport:OnAfterBackHome(From,Event,To,Carrier) -local carrier=Carrier -local warehouse=carrier:GetState(carrier,"WAREHOUSE") -carrier:SmokeWhite() -local text=string.format("Carrier %s is back home at warehouse %s.",tostring(Carrier:GetName()),tostring(warehouse.warehouse:GetName())) -MESSAGE:New(text,5):ToAllIf(warehouse.Debug) -warehouse:I(warehouse.lid..text) -warehouse:__Arrived(1,Carrier) -end -CargoTransport:__Start(5) -end -function WAREHOUSE:onafterUnloaded(From,Event,To,group) -self:_DebugMessage(string.format("Cargo %s unloaded!",tostring(group:GetName())),5) -if group and group:IsAlive()then -if self.Debug then -group:SmokeWhite() -end -local speedmax=group:GetSpeedMax() -if group:IsGround()then -if speedmax>1 then -group:RouteGroundTo(self.spawnzone:GetRandomCoordinate(),speedmax*0.5,AI.Task.VehicleFormation.RANK,3) -else -self:Arrived(group) -end -elseif group:IsAir()then -self:Arrived(group) -elseif group:IsShip()then -self:Arrived(group) -end -else -self:E(self.lid..string.format("ERROR unloaded Cargo group is not alive!")) -end -end -function WAREHOUSE:onbeforeArrived(From,Event,To,group) -local asset=self:FindAssetInDB(group) -if asset then -if asset.arrived==true then -return false -else -asset.arrived=true -return true -end -end -end -function WAREHOUSE:onafterArrived(From,Event,To,group) -if self.Debug then -group:SmokeOrange() -end -local request=self:_GetRequestOfGroup(group,self.pending) -if request then -local warehouse=request.warehouse -local istransport=self:_GroupIsTransport(group,request) -if istransport==true then -warehouse=self -elseif istransport==false then -warehouse=request.warehouse -else -self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport",group:GetName())) -return -end -self:_DebugMessage(string.format("Group %s arrived at warehouse %s!",tostring(group:GetName()),warehouse.alias),5) -if group:IsGround()and group:GetSpeedMax()>1 then -group:RouteGroundTo(warehouse:GetCoordinate(),group:GetSpeedMax()*0.3,"Off Road") -end -self:T(self.lid.."Asset arrived at warehouse adding in 60 sec") -warehouse:__AddAsset(60,group) -end -end -function WAREHOUSE:onafterDelivered(From,Event,To,request) -if self.verbosity>=1 then -local text=string.format("Warehouse %s: All assets delivered to warehouse %s!",self.alias,request.warehouse.alias) -self:_InfoMessage(text,5) -end -if self.Debug then -self:_Fireworks(request.warehouse:GetCoordinate()) -end -self.delivered[request.uid]=true -end -function WAREHOUSE:onafterSelfRequest(From,Event,To,groupset,request) -self:_DebugMessage(string.format("Assets spawned at warehouse %s after self request!",self.alias)) -for _,_group in pairs(groupset:GetSetObjects())do -local group=_group -if self.Debug then -group:FlareGreen() -end -end -if self:IsAttacked()then -if self.autodefence then -for _,_group in pairs(groupset:GetSetObjects())do -local group=_group -local speedmax=group:GetSpeedMax() -if group:IsGround()and speedmax>1 and group:IsNotInZone(self.zone)then -group:RouteGroundTo(self.zone:GetRandomCoordinate(),0.8*speedmax,"Off Road") -end -end -end -table.insert(self.defending,request) -end -end -function WAREHOUSE:onafterAttacked(From,Event,To,Coalition,Country) -local text=string.format("Warehouse %s: We are under attack!",self.alias) -self:_InfoMessage(text) -if self.Debug then -self:GetCoordinate():SmokeOrange() -end -if self.autodefence then -local nground=self:GetNumberOfAssets(WAREHOUSE.Descriptor.CATEGORY,Group.Category.GROUND) -local text=string.format("Warehouse auto defence activated.\n") -if nground>0 then -text=text..string.format("Deploying all %d ground assets.",nground) -self:AddRequest(self,WAREHOUSE.Descriptor.CATEGORY,Group.Category.GROUND,WAREHOUSE.Quantity.ALL,nil,nil,0,"AutoDefence") -else -text=text..string.format("No ground assets currently available.") -end -self:_InfoMessage(text) -else -local text=string.format("Warehouse auto defence inactive.") -self:I(self.lid..text) -end -end -function WAREHOUSE:onafterDefeated(From,Event,To) -local text=string.format("Warehouse %s: Enemy attack was defeated!",self.alias) -self:_InfoMessage(text) -if self.Debug then -self:GetCoordinate():SmokeGreen() -end -if self.autodefence then -for _,request in pairs(self.defending)do -for _,_group in pairs(request.cargogroupset:GetSetObjects())do -local group=_group -local speed=group:GetSpeedMax() -if group:IsGround()and speed>1 then -group:RouteGroundTo(self:GetCoordinate(),speed*0.3) -end -self:__AddAsset(60,group) -end -end -self.defending=nil -self.defending={} -end -end -function WAREHOUSE:onafterRespawn(From,Event,To) -local text=string.format("Respawning warehouse %s",self.alias) -self:_InfoMessage(text) -self.warehouse:ReSpawn() -end -function WAREHOUSE:onbeforeChangeCountry(From,Event,To,Country) -local currentCountry=self:GetCountry() -local text=string.format("Warehouse %s: request to change country %d-->%d",self.alias,currentCountry,Country) -self:_DebugMessage(text,10) -if currentCountry~=Country then -return true -end -return false -end -function WAREHOUSE:onafterChangeCountry(From,Event,To,Country) -local CoalitionOld=self:GetCoalition() -self.warehouse:ReSpawn(Country) -local CoalitionNew=self:GetCoalition() -self.queue=nil -self.queue={} -if self.airbasename then -local airbase=AIRBASE:FindByName(self.airbasename) -local airbaseCoalition=airbase:GetCoalition() -if CoalitionNew==airbaseCoalition then -self.airbase=airbase -else -self.airbase=nil -end -end -if self.Debug then -if CoalitionNew==coalition.side.RED then -self:GetCoordinate():SmokeRed() -elseif CoalitionNew==coalition.side.BLUE then -self:GetCoordinate():SmokeBlue() -end -end -end -function WAREHOUSE:onbeforeCaptured(From,Event,To,Coalition,Country) -self:ChangeCountry(Country) -end -function WAREHOUSE:onafterCaptured(From,Event,To,Coalition,Country) -local text=string.format("Warehouse %s: We were captured by enemy coalition (side=%d)!",self.alias,Coalition) -self:_InfoMessage(text) -end -function WAREHOUSE:onafterAirbaseCaptured(From,Event,To,Coalition) -local text=string.format("Warehouse %s: Our airbase %s was captured by the enemy (coalition=%d)!",self.alias,self.airbasename,Coalition) -self:_InfoMessage(text) -if self.Debug then -if Coalition==coalition.side.RED then -self.airbase:GetCoordinate():SmokeRed() -elseif Coalition==coalition.side.BLUE then -self.airbase:GetCoordinate():SmokeBlue() -end -end -self.airbase=nil -end -function WAREHOUSE:onafterAirbaseRecaptured(From,Event,To,Coalition) -local text=string.format("Warehouse %s: We recaptured our airbase %s from the enemy (coalition=%d)!",self.alias,self.airbasename,Coalition) -self:_InfoMessage(text) -self.airbase=AIRBASE:FindByName(self.airbasename) -if self.Debug then -if Coalition==coalition.side.RED then -self.airbase:GetCoordinate():SmokeRed() -elseif Coalition==coalition.side.BLUE then -self.airbase:GetCoordinate():SmokeBlue() -end -end -end -function WAREHOUSE:onafterRunwayDestroyed(From,Event,To) -local text=string.format("Warehouse %s: Runway %s destroyed!",self.alias,self.airbasename) -self:_InfoMessage(text) -self.runwaydestroyed=timer.getAbsTime() -end -function WAREHOUSE:onafterRunwayRepaired(From,Event,To) -local text=string.format("Warehouse %s: Runway %s repaired!",self.alias,self.airbasename) -self:_InfoMessage(text) -self.runwaydestroyed=nil -end -function WAREHOUSE:onbeforeAssetSpawned(From,Event,To,group,asset,request) -if asset.spawned then -else -end -return true -end -function WAREHOUSE:onafterAssetSpawned(From,Event,To,group,asset,request) -local text=string.format("Asset %s from request id=%d was spawned!",asset.spawngroupname,request.uid) -self:T(self.lid..text) -asset.spawned=true -local n=0 -for _,_asset in pairs(request.assets)do -local assetitem=_asset -self:T(self.lid..string.format("Asset %s spawned %s as %s",assetitem.templatename,tostring(assetitem.spawned),tostring(assetitem.spawngroupname))) -if assetitem.spawned then -n=n+1 -else -end -end -if n==request.nasset+request.ntransport then -self:T(self.lid..string.format("All assets %d (ncargo=%d + ntransport=%d) of request rid=%d spawned. Calling RequestSpawned",n,request.nasset,request.ntransport,request.uid)) -self:RequestSpawned(request,request.cargogroupset,request.transportgroupset) -else -self:T(self.lid..string.format("Not all assets %d (ncargo=%d + ntransport=%d) of request rid=%d spawned YET",n,request.nasset,request.ntransport,request.uid)) -end -end -function WAREHOUSE:onafterAssetDead(From,Event,To,asset,request) -local text=string.format("Asset %s from request id=%d is dead!",asset.templatename,request.uid) -self:T(self.lid..text) -self:_DebugMessage(text) -end -function WAREHOUSE:onafterDestroyed(From,Event,To) -local text=string.format("Warehouse %s was destroyed! Assets lost %d. Respawn=%s",self.alias,#self.stock,tostring(self.respawnafterdestroyed)) -self:_InfoMessage(text) -if self.respawnafterdestroyed then -if self.respawndelay then -self:Pause() -self:__Respawn(self.respawndelay) -else -self:Respawn() -end -else -for k,_ in pairs(self.queue)do -self.queue[k]=nil -end -for k,_ in pairs(self.stock)do -end -for k=#self.stock,1,-1 do -self.stock[k]=nil -end -end -end -function WAREHOUSE:onafterSave(From,Event,To,path,filename) -local function _savefile(filename,data) -local f=assert(io.open(filename,"wb")) -f:write(data) -f:close() -end -filename=filename or string.format("WAREHOUSE-%d_%s.txt",self.uid,self.alias) -if path~=nil then -filename=path.."\\"..filename -end -local text=string.format("Saving warehouse assets to file %s",filename) -MESSAGE:New(text,30):ToAllIf(self.Debug or self.Report) -self:I(self.lid..text) -local warehouseassets="" -warehouseassets=warehouseassets..string.format("coalition=%d\n",self:GetCoalition()) -warehouseassets=warehouseassets..string.format("country=%d\n",self:GetCountry()) -for _,_asset in pairs(self.stock)do -local asset=_asset -local assetstring="" -for key,value in pairs(asset)do -if key=="templatename"or key=="attribute"or key=="cargobay"or key=="weight"or key=="loadradius"or key=="livery"or key=="skill"or key=="assignment"then -local name -if type(value)=="table"then -name=string.format("%s=%s;",key,value[1]) -else -name=string.format("%s=%s;",key,value) -end -assetstring=assetstring..name -end -self:I(string.format("Loaded asset: %s",assetstring)) -end -warehouseassets=warehouseassets..assetstring.."\n" -end -_savefile(filename,warehouseassets) -end -function WAREHOUSE:onbeforeLoad(From,Event,To,path,filename) -local function _fileexists(name) -local f=io.open(name,"r") -if f~=nil then -io.close(f) -return true -else -return false -end -end -filename=filename or string.format("WAREHOUSE-%d_%s.txt",self.uid,self.alias) -if path~=nil then -filename=path.."\\"..filename -end -local exists=_fileexists(filename) -if exists then -return true -else -self:_ErrorMessage(string.format("ERROR: file %s does not exist! Cannot load assets.",filename),60) -return false -end -end -function WAREHOUSE:onafterLoad(From,Event,To,path,filename) -local function _loadfile(filename) -local f=assert(io.open(filename,"rb")) -local data=f:read("*all") -f:close() -return data -end -filename=filename or string.format("WAREHOUSE-%d_%s.txt",self.uid,self.alias) -if path~=nil then -filename=path.."\\"..filename -end -local text=string.format("Loading warehouse assets from file %s",filename) -MESSAGE:New(text,30):ToAllIf(self.Debug or self.Report) -self:I(self.lid..text) -local data=_loadfile(filename) -local assetdata=UTILS.Split(data,"\n") -local Coalition -local Country -local assets={} -for _,asset in pairs(assetdata)do -local descriptors=UTILS.Split(asset,";") -local asset={} -local isasset=false -for _,descriptor in pairs(descriptors)do -local keyval=UTILS.Split(descriptor,"=") -if#keyval==2 then -if keyval[1]=="coalition"then -Coalition=tonumber(keyval[2]) -elseif keyval[1]=="country"then -Country=tonumber(keyval[2]) -else -isasset=true -local key=keyval[1] -local val=keyval[2] -if val=="nil"then -val=nil -end -if key=="cargobay"or key=="weight"or key=="loadradius"then -asset[key]=tonumber(val) -else -asset[key]=val -end -end -end -end -if isasset then -table.insert(assets,asset) -end -end -if Country~=self:GetCountry()then -self:T(self.lid..string.format("Changing warehouse country %d-->%d on loading assets.",self:GetCountry(),Country)) -self:ChangeCountry(Country) -end -for _,_asset in pairs(assets)do -local asset=_asset -local group=GROUP:FindByName(asset.templatename) -if group then -self:AddAsset(group,1,asset.attribute,asset.cargobay,asset.weight,asset.loadradius,asset.skill,asset.livery,asset.assignment) -else -self:E(string.format("ERROR: Group %s doest not exit. Cannot be loaded as asset.",tostring(asset.templatename))) -end -end -end -function WAREHOUSE:_SpawnAssetRequest(Request) -self:F2({requestUID=Request.uid}) -local cargoassets=Request.cargoassets -local Parking={} -if Request.cargocategory==Group.Category.AIRPLANE or Request.cargocategory==Group.Category.HELICOPTER then -Parking=self:_FindParkingForAssets(self.airbase,cargoassets)or{} -end -local UnControlled=true -for i=1,#cargoassets do -local asset=cargoassets[i] -asset.spawned=false -asset.iscargo=true -asset.rid=Request.uid -local _alias=asset.spawngroupname -Request.assets[asset.uid]=asset -local _group=nil -if asset.category==Group.Category.GROUND then -_group=self:_SpawnAssetGroundNaval(_alias,asset,Request,self.spawnzone) -elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then -if Parking[asset.uid]then -_group=self:_SpawnAssetAircraft(_alias,asset,Request,Parking[asset.uid],UnControlled) -else -_group=self:_SpawnAssetAircraft(_alias,asset,Request,nil,UnControlled) -end -elseif asset.category==Group.Category.TRAIN then -if self.rail then -_group=self:_SpawnAssetGroundNaval(_alias,asset,Request,self.spawnzone) -end -elseif asset.category==Group.Category.SHIP then -_group=self:_SpawnAssetGroundNaval(_alias,asset,Request,self.portzone) -else -self:E(self.lid.."ERROR: Unknown asset category!") -end -end -end -function WAREHOUSE:_SpawnAssetGroundNaval(alias,asset,request,spawnzone,aioff) -if asset and(asset.category==Group.Category.GROUND or asset.category==Group.Category.SHIP or asset.category==Group.Category.TRAIN)then -local template=self:_SpawnAssetPrepareTemplate(asset,alias) -template.route.points[1]={} -local coord=spawnzone:GetRandomCoordinate() -if asset.category==Group.Category.TRAIN then -coord=self.rail -end -for i=1,#template.units do -local unit=template.units[i] -local SX=unit.x or 0 -local SY=unit.y or 0 -local BX=asset.template.route.points[1].x -local BY=asset.template.route.points[1].y -local TX=coord.x+(SX-BX) -local TY=coord.z+(SY-BY) -template.units[i].x=TX -template.units[i].y=TY -if asset.livery then -unit.livery_id=asset.livery -end -if asset.skill then -unit.skill=asset.skill -end -end -template.route.points[1].x=coord.x -template.route.points[1].y=coord.z -template.x=coord.x -template.y=coord.z -template.alt=coord.y -local group=_DATABASE:Spawn(template) -if aioff then -group:SetAIOff() -end -return group -end -return nil -end -function WAREHOUSE:_SpawnAssetAircraft(alias,asset,request,parking,uncontrolled,hotstart) -if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then -local template=self:_SpawnAssetPrepareTemplate(asset,alias) -if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then -if request.toself then -local wp=self.airbase:GetCoordinate():WaypointAir("RADIO",COORDINATE.WaypointType.TakeOffParking,COORDINATE.WaypointAction.FromParkingArea,0,false,self.airbase,{},"Parking") -template.route.points={wp} -else -template.route.points=self:_GetFlightplan(asset,self.airbase,request.warehouse.airbase) -end -else -local _type=COORDINATE.WaypointType.TakeOffParking -local _action=COORDINATE.WaypointAction.FromParkingArea -if hotstart then -_type=COORDINATE.WaypointType.TakeOffParkingHot -_action=COORDINATE.WaypointAction.FromParkingAreaHot -end -template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO",_type,_action,0,true,self.airbase,nil,"Spawnpoint") -end -local AirbaseID=self.airbase:GetID() -local AirbaseCategory=self:GetAirbaseCategory() -if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then -else -if#parking<#template.units then -local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.",#parking,#template.units) -self:_DebugMessage(text) -return nil -end -end -for i=1,#template.units do -local unit=template.units[i] -if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then -local coord=self.airbase:GetCoordinate() -unit.x=coord.x -unit.y=coord.z -unit.alt=coord.y -unit.parking_id=nil -unit.parking=nil -else -local coord=parking[i].Coordinate -local terminal=parking[i].TerminalID -if self.Debug then -coord:MarkToAll(string.format("Spawnplace unit %s terminal %d.",unit.name,terminal)) -end -unit.x=coord.x -unit.y=coord.z -unit.alt=coord.y -unit.parking_id=nil -unit.parking=terminal -end -if asset.livery then -unit.livery_id=asset.livery -end -if asset.skill then -unit.skill=asset.skill -end -if asset.payload then -unit.payload=asset.payload.pylons -end -if asset.modex then -unit.onboard_num=asset.modex[i] -end -if asset.callsign then -unit.callsign=asset.callsign[i] -end -end -template.x=template.units[1].x -template.y=template.units[1].y -template.uncontrolled=uncontrolled -self:T2({airtemplate=template}) -local group=_DATABASE:Spawn(template) -return group -end -return nil -end -function WAREHOUSE:_SpawnAssetPrepareTemplate(asset,alias) -local template=UTILS.DeepCopy(asset.template) -template.name=alias -template.CoalitionID=self:GetCoalition() -template.CountryID=self:GetCountry() -template.groupId=nil -template.lateActivation=false -if asset.missionTask then -self:T(self.lid..string.format("Setting mission task to %s",tostring(asset.missionTask))) -template.task=asset.missionTask -end -template.route={} -template.route.routeRelativeTOT=true -template.route.points={} -for i=1,#template.units do -local unit=template.units[i] -unit.unitId=nil -unit.name=string.format("%s-%02d",template.name,i) -end -return template -end -function WAREHOUSE:_RouteGround(group,request) -if group and group:IsAlive()then -local _speed=group:GetSpeedMax()*0.7 -local Waypoints={} -local hasoffroad=self:HasConnectionOffRoad(request.warehouse,self.Debug) -if hasoffroad then -local remotename=request.warehouse.warehouse:GetName() -local path=self.offroadpaths[remotename][math.random(#self.offroadpaths[remotename])] -for i=1,#path do -local coord=path[i] -local Waypoint=coord:WaypointGround(_speed,"Off Road") -table.insert(Waypoints,Waypoint) -end -else -Waypoints=group:TaskGroundOnRoad(request.warehouse.road,_speed,"Off Road",false,self.road) -local FromWP=group:GetCoordinate():WaypointGround(_speed,"Off Road") -table.insert(Waypoints,1,FromWP) -end -for n,wp in ipairs(Waypoints)do -env.info(n) -local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group,n,#Waypoints) -group:SetTaskWaypoint(wp,tf) -end -group:Route(Waypoints,1) -group:OptionROEReturnFire() -group:OptionAlarmStateGreen() -end -end -function WAREHOUSE:_RouteNaval(group,request) -if group and group:IsAlive()then -local _speed=group:GetSpeedMax()*0.8 -local remotename=request.warehouse.warehouse:GetName() -local lane=self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])] -if lane then -local Waypoints={} -for i=1,#lane do -local coord=lane[i] -local Waypoint=coord:WaypointGround(_speed) -table.insert(Waypoints,Waypoint) -end -local TaskFunction=self:_SimpleTaskFunction("warehouse:_Arrived",group) -local Waypoint=Waypoints[#Waypoints] -group:SetTaskWaypoint(Waypoint,TaskFunction) -group:Route(Waypoints,1) -group:OptionROEReturnFire() -else -self:E(self.lid..string.format("ERROR: No shipping lane defined for Naval asset!")) -end -end -end -function WAREHOUSE:_RouteAir(aircraft) -if aircraft and aircraft:IsAlive()~=nil then -self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s",aircraft:GetName(),tostring(aircraft:IsAlive()))) -local starttime=math.random(60) -aircraft:StartUncontrolled(starttime) -self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s (after start command)",aircraft:GetName(),tostring(aircraft:IsAlive()))) -aircraft:OptionROEReturnFire() -aircraft:OptionROTPassiveDefense() -else -self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!",tostring(aircraft:GetName()),tostring(aircraft:IsAlive()))) -end -end -function WAREHOUSE:_RouteTrain(Group,Coordinate,Speed) -if Group and Group:IsAlive()then -local _speed=Speed or Group:GetSpeedMax()*0.6 -local Waypoints=Group:TaskGroundOnRailRoads(Coordinate,Speed) -local TaskFunction=self:_SimpleTaskFunction("warehouse:_Arrived",Group) -local Waypoint=Waypoints[#Waypoints] -Group:SetTaskWaypoint(Waypoint,TaskFunction) -Group:Route(Waypoints,1) -end -end -function WAREHOUSE:_Arrived(group) -self:_DebugMessage(string.format("Group %s arrived!",tostring(group:GetName()))) -if group then -self:__Arrived(1,group) -end -end -function WAREHOUSE:_PassingWaypoint(group,n,N) -self:T(self.lid..string.format("Group %s passing waypoint %d of %d!",tostring(group:GetName()),n,N)) -if n==N then -self:__Arrived(1,group) -end -end -function WAREHOUSE:GetAssetByID(id) -if id then -return _WAREHOUSEDB.Assets[id] -else -return nil -end -end -function WAREHOUSE:GetAssetByName(GroupName) -local name=self:_GetNameWithOut(GroupName) -local _,aid,_=self:_GetIDsFromGroup(GROUP:FindByName(name)) -if aid then -return _WAREHOUSEDB.Assets[aid] -else -return nil -end -end -function WAREHOUSE:GetRequestByID(id) -if id then -for _,_request in pairs(self.queue)do -local request=_request -if request.uid==id then -return request,true -end -end -for _,_request in pairs(self.pending)do -local request=_request -if request.uid==id then -return request,false -end -end -end -return nil,nil -end -function WAREHOUSE:_OnEventBirth(EventData) -self:T3(self.lid..string.format("Warehouse %s (id=%s) captured event birth!",self.alias,self.uid)) -if EventData and EventData.IniGroup then -local group=EventData.IniGroup -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid==self.uid then -local asset=self:GetAssetByID(aid) -local request=self:GetRequestByID(rid) -if asset and request then -self:T(self.lid..string.format("Warehouse %s captured event birth of request ID=%d, asset ID=%d, unit %s spawned=%s",self.alias,request.uid,asset.uid,EventData.IniUnitName,tostring(asset.spawned))) -request.born=true -if not asset.spawned then -self:_DeleteStockItem(asset) -asset.spawned=true -asset.spawngroupname=group:GetName() -if asset.iscargo==true then -request.cargogroupset=request.cargogroupset or SET_GROUP:New() -request.cargogroupset:AddGroup(group) -else -request.transportgroupset=request.transportgroupset or SET_GROUP:New() -request.transportgroupset:AddGroup(group) -end -group:SetState(group,"WAREHOUSE",self) -self:AssetSpawned(group,asset,request) -end -else -self:E(self.lid..string.format("ERROR: Either asset AID=%s or request RID=%s are nil in event birth of unit %s",tostring(aid),tostring(rid),tostring(EventData.IniUnitName))) -end -else -end -end -end -function WAREHOUSE:_OnEventEngineStartup(EventData) -self:T3(self.lid..string.format("Warehouse %s captured event engine startup!",self.alias)) -if EventData and EventData.IniGroup then -local group=EventData.IniGroup -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid==self.uid then -self:T(self.lid..string.format("Warehouse %s captured event engine startup of its asset unit %s.",self.alias,EventData.IniUnitName)) -end -end -end -function WAREHOUSE:_OnEventTakeOff(EventData) -self:T3(self.lid..string.format("Warehouse %s captured event takeoff!",self.alias)) -if EventData and EventData.IniGroup then -local group=EventData.IniGroup -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid==self.uid then -self:T(self.lid..string.format("Warehouse %s captured event takeoff of its asset unit %s.",self.alias,EventData.IniUnitName)) -end -end -end -function WAREHOUSE:_OnEventLanding(EventData) -self:T3(self.lid..string.format("Warehouse %s captured event landing!",self.alias)) -if EventData and EventData.IniGroup then -local group=EventData.IniGroup -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid~=nil and wid==self.uid then -self:T(self.lid..string.format("Warehouse %s captured event landing of its asset unit %s.",self.alias,EventData.IniUnitName)) -end -end -end -function WAREHOUSE:_OnEventEngineShutdown(EventData) -self:T3(self.lid..string.format("Warehouse %s captured event engine shutdown!",self.alias)) -if EventData and EventData.IniGroup then -local group=EventData.IniGroup -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid==self.uid then -self:T(self.lid..string.format("Warehouse %s captured event engine shutdown of its asset unit %s.",self.alias,EventData.IniUnitName)) -end -end -end -function WAREHOUSE:_OnEventArrived(EventData) -if EventData and EventData.IniUnit then -local unit=EventData.IniUnit -if unit and unit:IsAlive()==true and unit:InAir()==false then -local group=EventData.IniGroup -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid~=nil and aid~=nil and rid~=nil then -if self.uid==wid then -local request=self:_GetRequestOfGroup(group,self.pending) -if request then -local istransport=self:_GroupIsTransport(group,request) -local closest=group:GetCoordinate():GetClosestAirbase() -local rightairbase=closest:GetName()==request.warehouse:GetAirbase():GetName() -if istransport==false and rightairbase then -local nunits=#group:GetUnits() -local dt=10*(nunits-1)+1 -if self.verbosity>=1 then -local text=string.format("Air asset group %s from warehouse %s arrived at its destination. Trigger Arrived event in %d sec",group:GetName(),self.alias,dt) -self:_InfoMessage(text) -end -self:__Arrived(dt,group) -end -end -end -else -self:T3(string.format("Group that arrived did not belong to a warehouse. Warehouse ID=%s, Asset ID=%s, Request ID=%s.",tostring(wid),tostring(aid),tostring(rid))) -end -end -end -end -function WAREHOUSE:_OnEventCrashOrDead(EventData) -self:T3(self.lid..string.format("Warehouse %s captured event dead or crash!",self.alias)) -if EventData then -if EventData.IniUnitName then -local warehousename=self.warehouse:GetName() -if EventData.IniUnitName==warehousename then -self:_DebugMessage(string.format("Warehouse %s alias %s was destroyed!",warehousename,self.alias)) -self:Destroyed() -end -if self.airbase and self.airbasename and self.airbasename==EventData.IniUnitName then -self:RunwayDestroyed() -end -end -if EventData.IniGroup then -local group=EventData.IniGroup -local wid,aid,rid=self:_GetIDsFromGroup(group) -if wid==self.uid then -self:T(self.lid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.",self.alias,EventData.IniUnitName)) -for _,request in pairs(self.pending)do -local request=request -if request.uid==rid then -self:_UnitDead(EventData.IniUnit,request) -end -end -end -end -end -end -function WAREHOUSE:_UnitDead(deadunit,request) -if self.Debug then -deadunit:FlareRed() -end -local group=deadunit:GetGroup() -local nalive=group:CountAliveUnits() -local groupdead=true -if nalive>0 then -groupdead=false -end -local unitname=self:_GetNameWithOut(deadunit) -local groupname=self:_GetNameWithOut(group) -if groupdead then -self:T(self.lid..string.format("Group %s (transport=%s) is dead!",groupname,tostring(self:_GroupIsTransport(group,request)))) -if self.Debug then -group:SmokeWhite() -end -local asset=self:FindAssetInDB(group) -self:AssetDead(asset,request) -end -local NoTriggerEvent=true -if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then -if groupdead==true then -request.cargogroupset:Remove(groupname,NoTriggerEvent) -self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.",groupname,request.cargogroupset:Count())) -end -else -local istransport=self:_GroupIsTransport(group,request) -if istransport==true then -local cargogroupnames=request.carriercargo[unitname] -if cargogroupnames then -for _,cargoname in pairs(cargogroupnames)do -request.cargogroupset:Remove(cargoname,NoTriggerEvent) -self:T(self.lid..string.format("Removed transported cargo %s inside dead carrier %s: ncargo=%d",cargoname,unitname,request.cargogroupset:Count())) -end -end -if groupdead then -request.transportgroupset:Remove(groupname,NoTriggerEvent) -self:T(self.lid..string.format("Removed transport %s: ntransport=%d",groupname,request.transportgroupset:Count())) -end -elseif istransport==false then -if groupdead==true then -request.cargogroupset:Remove(groupname,NoTriggerEvent) -self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d",groupname,request.cargogroupset:Count())) -end -else -self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!",group:GetName())) -end -end -end -function WAREHOUSE:_OnEventBaseCaptured(EventData) -self:T3(self.lid..string.format("Warehouse %s captured event base captured!",self.alias)) -if self.airbasename==nil then -return -end -if EventData and EventData.Place then -local airbase=EventData.Place -if EventData.PlaceName==self.airbasename then -local NewCoalitionAirbase=airbase:GetCoalition() -self:T(self.lid..string.format("Airbase of warehouse %s (coalition ID=%d) was captured! New owner coalition ID=%d.",self.alias,self:GetCoalition(),NewCoalitionAirbase)) -if self.airbase==nil then -if NewCoalitionAirbase==self:GetCoalition()then -self:AirbaseRecaptured(NewCoalitionAirbase) -end -else -if NewCoalitionAirbase~=self:GetCoalition()then -self:AirbaseCaptured(NewCoalitionAirbase) -end -end -end -end -end -function WAREHOUSE:_OnEventMissionEnd(EventData) -self:T3(self.lid..string.format("Warehouse %s captured event mission end!",self.alias)) -if self.autosave then -self:Save(self.autosavepath,self.autosavefile) -end -end -function WAREHOUSE:_CheckConquered() -local coord=self.zone:GetCoordinate() -local radius=self.zone:GetRadius() -local gotunits,_,_,units,_,_=coord:ScanObjects(radius,true,false,false) -local Nblue=0 -local Nred=0 -local Nneutral=0 -local CountryBlue=nil -local CountryRed=nil -local CountryNeutral=nil -if gotunits then -for _,_unit in pairs(units)do -local unit=_unit -local distance=coord:Get2DDistance(unit:GetCoordinate()) -if unit:IsGround()and unit:IsAlive()and distance<=radius then -local _coalition=unit:GetCoalition() -local _country=unit:GetCountry() -self:T2(self.lid..string.format("Unit %s in warehouse zone of radius=%d m. Coalition=%d, country=%d. Distance = %d m.",unit:GetName(),radius,_coalition,_country,distance)) -if _coalition==coalition.side.BLUE then -Nblue=Nblue+1 -CountryBlue=_country -elseif _coalition==coalition.side.RED then -Nred=Nred+1 -CountryRed=_country -else -Nneutral=Nneutral+1 -CountryNeutral=_country -end -end -end -end -self:T(self.lid..string.format("Ground troops in warehouse zone: blue=%d, red=%d, neutral=%d",Nblue,Nred,Nneutral)) -local newcoalition=self:GetCoalition() -local newcountry=self:GetCountry() -if Nblue>0 and Nred==0 and Nneutral==0 then -newcoalition=coalition.side.BLUE -newcountry=CountryBlue -elseif Nblue==0 and Nred>0 and Nneutral==0 then -newcoalition=coalition.side.RED -newcountry=CountryRed -elseif Nblue==0 and Nred==0 and Nneutral>0 then -end -if self:IsAttacked()and newcoalition~=self:GetCoalition()then -self:Captured(newcoalition,newcountry) -return -end -if self:GetCoalition()==coalition.side.BLUE then -if self:IsRunning()and Nred>0 then -self:Attacked(coalition.side.RED,CountryRed) -end -if self:IsAttacked()and Nred==0 then -self:Defeated() -end -elseif self:GetCoalition()==coalition.side.RED then -if self:IsRunning()and Nblue>0 then -self:Attacked(coalition.side.BLUE,CountryBlue) -end -if self:IsAttacked()and Nblue==0 then -self:Defeated() -end -elseif self:GetCoalition()==coalition.side.NEUTRAL then -if self:IsRunning()and Nred>0 then -self:Attacked(coalition.side.RED,CountryRed) -elseif self:IsRunning()and Nblue>0 then -self:Attacked(coalition.side.BLUE,CountryBlue) -end -end -end -function WAREHOUSE:_CheckAirbaseOwner() -if self.airbasename then -local airbase=AIRBASE:FindByName(self.airbasename) -local airbasecurrentcoalition=airbase:GetCoalition() -if self.airbase then -if self:GetCoalition()~=airbasecurrentcoalition then -self.airbase=nil -end -else -if self:GetCoalition()==airbasecurrentcoalition then -self.airbase=airbase -end -end -end -end -function WAREHOUSE:_CheckRequestConsistancy(queue) -self:T3(self.lid..string.format("Number of queued requests = %d",#queue)) -local invalid={} -for _,_request in pairs(queue)do -local request=_request -self:T2(self.lid..string.format("Checking request id=%d.",request.uid)) -local valid=true -if request.nasset==0 then -self:E(self.lid..string.format("ERROR: INVALID request. Request for zero assets not possible. Can happen when, e.g. \"all\" ground assets are requests but none in stock.")) -valid=false -end -if self:GetCoalition()~=request.warehouse:GetCoalition()then -self:E(self.lid..string.format("ERROR: INVALID request. Requesting warehouse is of wrong coaltion! Own coalition %s != %s of requesting warehouse.",self:GetCoalitionName(),request.warehouse:GetCoalitionName())) -valid=false -end -if request.warehouse:IsStopped()then -self:E(self.lid..string.format("ERROR: INVALID request. Requesting warehouse is stopped!")) -valid=false -end -if request.warehouse:IsDestroyed()and not self.respawnafterdestroyed then -self:E(self.lid..string.format("ERROR: INVALID request. Requesting warehouse is destroyed!")) -valid=false -end -if valid==false then -self:E(self.lid..string.format("Got invalid request id=%d.",request.uid)) -table.insert(invalid,request) -else -self:T3(self.lid..string.format("Got valid request id=%d.",request.uid)) -end -end -for _,_request in pairs(invalid)do -self:E(self.lid..string.format("Deleting INVALID request id=%d.",_request.uid)) -self:_DeleteQueueItem(_request,self.queue) -end -end -function WAREHOUSE:_CheckRequestValid(request) -local _assets,_nassets,_enough=self:_FilterStock(self.stock,request.assetdesc,request.assetdescval,request.nasset) -if#_assets==0 then -return true -end -local nasset=request.nasset -if type(request.nasset)=="string"then -nasset=self:_QuantityRel2Abs(request.nasset,_nassets) -end -local text=string.format("Request valid? Number of assets: requested=%s=%d, selected=%d, total=%d, enough=%s.",tostring(request.nasset),nasset,#_assets,_nassets,tostring(_enough)) -self:T(text) -local asset=_assets[1] -local asset_plane=asset.category==Group.Category.AIRPLANE -local asset_helo=asset.category==Group.Category.HELICOPTER -local asset_ground=asset.category==Group.Category.GROUND -local asset_train=asset.category==Group.Category.TRAIN -local asset_naval=asset.category==Group.Category.SHIP -local asset_air=asset_helo or asset_plane -local valid=true -local requestcategory=request.warehouse:GetAirbaseCategory() -if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then -if asset_air then -if asset_plane then -if requestcategory==Airbase.Category.HELIPAD or self:GetAirbaseCategory()==Airbase.Category.HELIPAD then -self:E("ERROR: Incorrect request. Asset airplane requested but warehouse or requestor is HELIPAD/FARP!") -valid=false -end -elseif asset_helo then -if self:GetAirbaseCategory()==-1 or requestcategory==-1 then -self:E("ERROR: Incorrect request. Helos need a AIRBASE/HELIPAD/SHIP as home/destination base!") -valid=false -end -end -if self.airbase==nil or request.airbase==nil then -self:E("ERROR: Incorrect request. Either warehouse or requesting warehouse does not have any kind of airbase!") -valid=false -else -local termtype_dep=asset.terminalType or self:_GetTerminal(asset.attribute,self:GetAirbaseCategory()) -local termtype_des=asset.terminalType or self:_GetTerminal(asset.attribute,request.warehouse:GetAirbaseCategory()) -local np_departure=self.airbase:GetParkingSpotsNumber(termtype_dep) -local np_destination=request.airbase:GetParkingSpotsNumber(termtype_des) -self:T(string.format("Asset attribute = %s, DEPARTURE: terminal type = %d, spots = %d, DESTINATION: terminal type = %d, spots = %d",asset.attribute,termtype_dep,np_departure,termtype_des,np_destination)) -if np_departure0 then -_assetattribute=_assets[1].attribute -_assetcategory=_assets[1].category -if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then -if self.airbase and self.airbase:GetCoalition()==self:GetCoalition()then -if self:IsRunwayOperational()then -local Parking=self:_FindParkingForAssets(self.airbase,_assets) -if Parking==nil then -local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all requested assets at the moment.",self.alias) -self:_InfoMessage(text,5) -return false -end -else -local text=string.format("Warehouse %s: Request denied! Runway is still destroyed",self.alias) -self:_InfoMessage(text,5) -return false -end -else -local text=string.format("Warehouse %s: Request denied! No airbase",self.alias) -self:_InfoMessage(text,5) -return false -end -end -request.cargoassets=_assets -end -if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then -_transports=self:_GetTransportsForAssets(request) -if#_transports>0 then -local _transportattribute=_transports[1].attribute -local _transportcategory=_transports[1].category -if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then -if self.airbase and self.airbase:GetCoalition()==self:GetCoalition()then -if self:IsRunwayOperational()then -local Parking=self:_FindParkingForAssets(self.airbase,_transports) -if Parking==nil then -local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.",self.alias) -self:_InfoMessage(text,5) -return false -end -else -local text=string.format("Warehouse %s: Request denied! Runway is still destroyed",self.alias) -self:_InfoMessage(text,5) -return false -end -else -local text=string.format("Warehouse %s: Request denied! No airbase currently!",self.alias) -self:_InfoMessage(text,5) -return false -end -end -else -local text=string.format("Warehouse %s: Request denied! Not enough transport carriers available at the moment.",self.alias) -self:_InfoMessage(text,5) -return false -end -else -if _assetcategory==Group.Category.GROUND then -local dist=self.warehouse:GetCoordinate():Get2DDistance(self.spawnzone:GetCoordinate()) -if dist>self.spawnzonemaxdist then -local text=string.format("Warehouse %s: Request denied! Not close enough to spawn zone. Distance = %d m. We need to be at least within %d m range to spawn.",self.alias,dist,self.spawnzonemaxdist) -self:_InfoMessage(text,5) -return false -end -end -end -request.cargoassets=_assets -request.cargoattribute=_assets[1].attribute -request.cargocategory=_assets[1].category -request.nasset=#_assets -local text=string.format("Selected cargo assets, attibute=%s, category=%d:\n",request.cargoattribute,request.cargocategory) -for _i,_asset in pairs(_assets)do -local asset=_asset -text=text..string.format("%d) name=%s, type=%s, category=%d, #units=%d",_i,asset.templatename,asset.unittype,asset.category,asset.nunits) -end -self:T(self.lid..text) -if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then -request.transportassets=_transports -request.transportattribute=_transports[1].attribute -request.transportcategory=_transports[1].category -request.ntransport=#_transports -local text=string.format("Selected transport assets, attibute=%s, category=%d:\n",request.transportattribute,request.transportcategory) -for _i,_asset in pairs(_transports)do -local asset=_asset -text=text..string.format("%d) name=%s, type=%s, category=%d, #units=%d\n",_i,asset.templatename,asset.unittype,asset.category,asset.nunits) -end -self:T(self.lid..text) -end -return true -end -function WAREHOUSE:_GetTransportsForAssets(request) -local transports=self:_FilterStock(self.stock,WAREHOUSE.Descriptor.ATTRIBUTE,request.transporttype,nil,true) -local cargoassets=UTILS.DeepCopy(request.cargoassets) -local cargoset=request.transportcargoset -local function sort_transports(a,b) -return a.cargobaymax>b.cargobaymax -end -local function sort_cargoassets(a,b) -return a.weight>b.weight -end -table.sort(transports,sort_transports) -table.sort(cargoassets,sort_cargoassets) -self:T2(self.lid.."Transport capability:") -local totalbay=0 -for i=1,#transports do -local transport=transports[i] -for j=1,transport.nunits do -totalbay=totalbay+transport.cargobay[j] -self:T2(self.lid..string.format("Cargo bay = %d (unit=%d)",transport.cargobay[j],j)) -end -end -self:T2(self.lid..string.format("Total capacity = %d",totalbay)) -self:T2(self.lid.."Cargo weight:") -local totalcargoweight=0 -for i=1,#cargoassets do -local asset=cargoassets[i] -totalcargoweight=totalcargoweight+asset.weight -self:T2(self.lid..string.format("weight = %d",asset.weight)) -end -self:T2(self.lid..string.format("Total weight = %d",totalcargoweight)) -local used_transports={} -for i=1,#transports do -local transport=transports[i] -local putintocarrier={} -local used=false -for k=1,transport.nunits do -local cargobay=transport.cargobay[k] -for j,asset in pairs(cargoassets)do -local asset=asset -local delta=cargobay-asset.weight -if delta>=0 then -cargobay=cargobay-asset.weight -self:T3(self.lid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d",transport.templatename,k,asset.uid,transport.cargobay[k],cargobay,asset.weight)) -table.insert(putintocarrier,j) -used=true -else -self:T2(self.lid..string.format("Carrier unit %s too small for cargo asset %s ==> cannot be used! Cargo bay - asset weight = %d kg",transport.templatename,asset.templatename,delta)) -end -end -end -for j=#putintocarrier,1,-1 do -local nput=putintocarrier[j] -local cargo=cargoassets[nput] -if cargo then -self:T2(self.lid..string.format("Cargo id=%d assigned for carrier id=%d",cargo.uid,transport.uid)) -table.remove(cargoassets,nput) -end -end -if used then -table.insert(used_transports,transport) -end -local ntrans=self:_QuantityRel2Abs(request.ntransport,#transports) -if#used_transports>=ntrans then -request.ntransport=#used_transports -break -end -end -local text=string.format("Used Transports for request %d to warehouse %s:\n",request.uid,request.warehouse.alias) -local totalcargobay=0 -for _i,_transport in pairs(used_transports)do -local transport=_transport -text=text..string.format("%d) %s: cargobay tot = %d kg, cargobay max = %d kg, nunits=%d\n",_i,transport.unittype,transport.cargobaytot,transport.cargobaymax,transport.nunits) -totalcargobay=totalcargobay+transport.cargobaytot -end -text=text..string.format("Total cargo bay capacity = %.1f kg\n",totalcargobay) -text=text..string.format("Total cargo weight = %.1f kg\n",totalcargoweight) -text=text..string.format("Minimum number of runs = %.1f",totalcargoweight/totalcargobay) -self:_DebugMessage(text) -return used_transports -end -function WAREHOUSE:_QuantityRel2Abs(relative,ntot) -local nabs=0 -if type(relative)=="string"then -if relative==WAREHOUSE.Quantity.ALL then -nabs=ntot -elseif relative==WAREHOUSE.Quantity.THREEQUARTERS then -nabs=UTILS.Round(ntot*3/4) -elseif relative==WAREHOUSE.Quantity.HALF then -nabs=UTILS.Round(ntot/2) -elseif relative==WAREHOUSE.Quantity.THIRD then -nabs=UTILS.Round(ntot/3) -elseif relative==WAREHOUSE.Quantity.QUARTER then -nabs=UTILS.Round(ntot/4) -else -nabs=math.min(1,ntot) -end -else -nabs=relative -end -self:T2(self.lid..string.format("Relative %s: tot=%d, abs=%.2f",tostring(relative),ntot,nabs)) -return nabs -end -function WAREHOUSE:_CheckQueue() -self:_SortQueue() -local request=nil -local invalid={} -local gotit=false -for _,_qitem in ipairs(self.queue)do -local qitem=_qitem -local valid=self:_CheckRequestValid(qitem) -local okay=false -if valid then -okay=self:_CheckRequestNow(qitem) -else -table.insert(invalid,qitem) -end -if okay and valid and not gotit then -request=qitem -gotit=true -break -end -end -for _,_request in pairs(invalid)do -self:T(self.lid..string.format("Deleting invalid request id=%d.",_request.uid)) -self:_DeleteQueueItem(_request,self.queue) -end -return request -end -function WAREHOUSE:_SimpleTaskFunction(Function,group) -self:F2({Function}) -local warehouse=self.warehouse:GetName() -local groupname=group:GetName() -local DCSScript={} -DCSScript[#DCSScript+1]=string.format('local mygroup = GROUP:FindByName(\"%s\") ',groupname) -if self.isunit then -DCSScript[#DCSScript+1]=string.format("local mywarehouse = UNIT:FindByName(\"%s\") ",warehouse) -else -DCSScript[#DCSScript+1]=string.format("local mywarehouse = STATIC:FindByName(\"%s\") ",warehouse) -end -DCSScript[#DCSScript+1]=string.format('local warehouse = mywarehouse:GetState(mywarehouse, \"WAREHOUSE\") ') -DCSScript[#DCSScript+1]=string.format('%s(mygroup)',Function) -local DCSTask=CONTROLLABLE.TaskWrappedAction(self,CONTROLLABLE.CommandDoScript(self,table.concat(DCSScript))) -return DCSTask -end -function WAREHOUSE:_SimpleTaskFunctionWP(Function,group,n,N) -self:F2({Function}) -local warehouse=self.warehouse:GetName() -local groupname=group:GetName() -local DCSScript={} -DCSScript[#DCSScript+1]=string.format('local mygroup = GROUP:FindByName(\"%s\") ',groupname) -if self.isunit then -DCSScript[#DCSScript+1]=string.format("local mywarehouse = UNIT:FindByName(\"%s\") ",warehouse) -else -DCSScript[#DCSScript+1]=string.format("local mywarehouse = STATIC:FindByName(\"%s\") ",warehouse) -end -DCSScript[#DCSScript+1]=string.format('local warehouse = mywarehouse:GetState(mywarehouse, \"WAREHOUSE\") ') -DCSScript[#DCSScript+1]=string.format('%s(mygroup, %d, %d)',Function,n,N) -local DCSTask=CONTROLLABLE.TaskWrappedAction(self,CONTROLLABLE.CommandDoScript(self,table.concat(DCSScript))) -return DCSTask -end -function WAREHOUSE:_GetTerminal(_attribute,_category) -local _terminal=AIRBASE.TerminalType.OpenBig -if _attribute==WAREHOUSE.Attribute.AIR_FIGHTER then -_terminal=AIRBASE.TerminalType.FighterAircraft -elseif _attribute==WAREHOUSE.Attribute.AIR_BOMBER or _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTPLANE or _attribute==WAREHOUSE.Attribute.AIR_TANKER or _attribute==WAREHOUSE.Attribute.AIR_AWACS then -_terminal=AIRBASE.TerminalType.OpenBig -elseif _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO then -_terminal=AIRBASE.TerminalType.HelicopterUsable -else -end -if _category==Airbase.Category.SHIP then -if not(_attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO)then -_terminal=AIRBASE.TerminalType.OpenMedOrBig -end -end -return _terminal -end -function WAREHOUSE:_FindParkingForAssets(airbase,assets) -local scanradius=25 -local scanunits=true -local scanstatics=true -local scanscenery=false -local verysafe=false -local function _overlap(l1,l2,dist) -local safedist=(l1/2+l2/2)*1.05 -local safe=(dist>safedist) -self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s",l1,l2,safedist,dist,tostring(safe))) -return safe -end -local function _clients() -local clients=_DATABASE.CLIENTS -local coords={} -for clientname,client in pairs(clients)do -local template=_DATABASE:GetGroupTemplateFromUnitName(clientname) -local units=template.units -for i,unit in pairs(units)do -local coord=COORDINATE:New(unit.x,unit.alt,unit.y) -coords[unit.name]=coord -end -end -return coords -end -local parkingdata=airbase.parking -local obstacles={} -self.clientcoords=self.clientcoords or _clients() -for clientname,_coord in pairs(self.clientcoords)do -table.insert(obstacles,{coord=_coord,size=15,name=clientname,type="client"}) -end -for _,parkingspot in pairs(parkingdata)do -local _spot=parkingspot.Coordinate -local _termid=parkingspot.TerminalID -local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius,scanunits,scanstatics,scanscenery) -for _,_unit in pairs(_units)do -local unit=_unit -local _coord=unit:GetVec3() -local _size=self:_GetObjectSize(unit:GetDCSObject()) -local _name=unit:GetName() -table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="unit"}) -end -for _,static in pairs(_statics)do -local _coord=static:getPoint() -local _name=static:getName() -local _size=self:_GetObjectSize(static) -table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="static"}) -end -for _,scenery in pairs(_sceneries)do -local _coord=scenery:getPoint() -local _name=scenery:getTypeName() -local _size=self:_GetObjectSize(scenery) -table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="scenery"}) -end -end -local parking={} -for _,asset in pairs(assets)do -local _asset=asset -local terminaltype=asset.terminalType or self:_GetTerminal(asset.attribute,self:GetAirbaseCategory()) -parking[_asset.uid]={} -for i=1,_asset.nunits do -local gotit=false -for _,_parkingspot in pairs(parkingdata)do -local parkingspot=_parkingspot -if AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype)and self:_CheckParkingValid(parkingspot,airbase)and airbase:_CheckParkingLists(parkingspot.TerminalID)then -local _spot=parkingspot.Coordinate -local _termid=parkingspot.TerminalID -local free=true -local problem=nil -for _,obstacle in pairs(obstacles)do -local dist=_spot:Get2DDistance(obstacle.coord) -local safe=_overlap(_asset.size,obstacle.size,dist) -if not safe then -free=false -problem=obstacle -problem.dist=dist -break -else -end -end -if free then -table.insert(parking[_asset.uid],parkingspot) -self:T(self.lid..string.format("Parking spot %d is free for asset id=%d!",_termid,_asset.uid)) -table.insert(obstacles,{coord=_spot,size=_asset.size,name=_asset.templatename,type="asset"}) -gotit=true -break -else -self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!",_termid)) -if self.Debug then -local coord=problem.coord -local text=string.format("Obstacle blocking spot #%d is %s type %s with size=%.1f m and distance=%.1f m.",_termid,problem.name,problem.type,problem.size,problem.dist) -coord:MarkToAll(string.format(text)) -end -end -end -end -if not gotit then -self:I(self.lid..string.format("WARNING: No free parking spot for asset id=%d",_asset.uid)) -return nil -end -end -end -return parking -end -function WAREHOUSE:_GetRequestOfGroup(group,queue) -local wid,aid,rid=self:_GetIDsFromGroup(group) -for _,_request in pairs(queue)do -local request=_request -if request.uid==rid then -return request -end -end -end -function WAREHOUSE:_GroupIsTransport(group,request) -local asset=self:FindAssetInDB(group) -if asset and asset.iscargo~=nil then -return not asset.iscargo -else -local groupname=self:_GetNameWithOut(group) -if request.transportgroupset then -local transporters=request.transportgroupset:GetSetObjects() -for _,transport in pairs(transporters)do -if transport:GetName()==groupname then -return true -end -end -end -if request.cargogroupset then -local cargos=request.cargogroupset:GetSetObjects() -for _,cargo in pairs(cargos)do -if self:_GetNameWithOut(cargo)==groupname then -return false -end -end -end -end -return nil -end -function WAREHOUSE:_GetNameWithOut(group) -local groupname=type(group)=="string"and group or group:GetName() -if groupname:find("CARGO")then -local name=groupname:gsub("#CARGO","") -return name -else -return groupname -end -end -function WAREHOUSE:_GetIDsFromGroup(group) -local function analyse(text) -local unspawned=UTILS.Split(text,"#")[1] -local keywords=UTILS.Split(unspawned,"_") -local _wid=nil -local _aid=nil -local _rid=nil -for _,keys in pairs(keywords)do -local str=UTILS.Split(keys,"-") -local key=str[1] -local val=str[2] -if key:find("WID")then -_wid=tonumber(val) -elseif key:find("AID")then -_aid=tonumber(val) -elseif key:find("RID")then -_rid=tonumber(val) -end -end -return _wid,_aid,_rid -end -if group then -local name=group:GetName() -local wid,aid,rid=analyse(name) -local asset=self:GetAssetByID(aid) -if asset then -wid=asset.wid -rid=asset.rid -end -self:T(self.lid..string.format("Group Name = %s",tostring(name))) -self:T(self.lid..string.format("Warehouse ID = %s",tostring(wid))) -self:T(self.lid..string.format("Asset ID = %s",tostring(aid))) -self:T(self.lid..string.format("Request ID = %s",tostring(rid))) -return wid,aid,rid -else -self:E("WARNING: Group not found in GetIDsFromGroup() function!") -end -end -function WAREHOUSE:_GetIDsFromGroupOLD(group) -local function analyse(text) -local unspawned=UTILS.Split(text,"#")[1] -local keywords=UTILS.Split(unspawned,"_") -local _wid=nil -local _aid=nil -local _rid=nil -for _,keys in pairs(keywords)do -local str=UTILS.Split(keys,"-") -local key=str[1] -local val=str[2] -if key:find("WID")then -_wid=tonumber(val) -elseif key:find("AID")then -_aid=tonumber(val) -elseif key:find("RID")then -_rid=tonumber(val) -end -end -return _wid,_aid,_rid -end -if group then -local name=group:GetName() -local wid,aid,rid=analyse(name) -self:T3(self.lid..string.format("Group Name = %s",tostring(name))) -self:T3(self.lid..string.format("Warehouse ID = %s",tostring(wid))) -self:T3(self.lid..string.format("Asset ID = %s",tostring(aid))) -self:T3(self.lid..string.format("Request ID = %s",tostring(rid))) -return wid,aid,rid -else -self:E("WARNING: Group not found in GetIDsFromGroup() function!") -end -end -function WAREHOUSE:FilterStock(descriptor,attribute,nmax,mobile) -return self:_FilterStock(self.stock,descriptor,attribute,nmax,mobile) -end -function WAREHOUSE:_FilterStock(stock,descriptor,attribute,nmax,mobile) -nmax=nmax or WAREHOUSE.Quantity.ALL -if mobile==nil then -mobile=false -end -local filtered={} -if descriptor==WAREHOUSE.Descriptor.ASSETLIST then -local ntot=0 -for _,_rasset in pairs(attribute)do -local rasset=_rasset -for _,_asset in ipairs(stock)do -local asset=_asset -if rasset.uid==asset.uid then -table.insert(filtered,asset) -break -end -end -end -return filtered,#filtered,#filtered>=#attribute -end -local ntot=0 -for _,_asset in ipairs(stock)do -local asset=_asset -local ismobile=asset.speedmax>0 -if asset[descriptor]==attribute then -if(mobile==true and ismobile)or mobile==false then -ntot=ntot+1 -end -end -end -if ntot==0 then -return filtered,ntot,false -end -nmax=self:_QuantityRel2Abs(nmax,ntot) -for _i,_asset in ipairs(stock)do -local asset=_asset -if asset[descriptor]==attribute then -if(mobile and asset.speedmax>0)or(not mobile)then -table.insert(filtered,asset) -if nmax~=nil and#filtered>=nmax then -return filtered,ntot,true -end -end -end -end -return filtered,ntot,ntot>=nmax -end -function WAREHOUSE:_HasAttribute(group,attribute) -if group then -local groupattribute=self:_GetAttribute(group) -return groupattribute==attribute -end -return false -end -function WAREHOUSE:_GetAttribute(group) -local attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN -if group then -local transportplane=group:HasAttribute("Transports")and group:HasAttribute("Planes") -local awacs=group:HasAttribute("AWACS") -local fighter=group:HasAttribute("Fighters")or group:HasAttribute("Interceptors")or group:HasAttribute("Multirole fighters")or(group:HasAttribute("Bombers")and not group:HasAttribute("Strategic bombers")) -local bomber=group:HasAttribute("Strategic bombers") -local tanker=group:HasAttribute("Tankers") -local uav=group:HasAttribute("UAVs") -local transporthelo=group:HasAttribute("Transport helicopters") -local attackhelicopter=group:HasAttribute("Attack helicopters") -local apc=group:HasAttribute("Infantry carriers") -local truck=group:HasAttribute("Trucks")and group:GetCategory()==Group.Category.GROUND -local infantry=group:HasAttribute("Infantry") -local artillery=group:HasAttribute("Artillery") -local tank=group:HasAttribute("Old Tanks")or group:HasAttribute("Modern Tanks") -local aaa=group:HasAttribute("AAA") -local ewr=group:HasAttribute("EWR") -local sam=group:HasAttribute("SAM elements")and(not group:HasAttribute("AAA")) -local train=group:GetCategory()==Group.Category.TRAIN -local aircraftcarrier=group:HasAttribute("Aircraft Carriers") -local warship=group:HasAttribute("Heavy armed ships") -local armedship=group:HasAttribute("Armed ships")or group:HasAttribute("Armed Ship") -local unarmedship=group:HasAttribute("Unarmed ships") -if transportplane then -attribute=WAREHOUSE.Attribute.AIR_TRANSPORTPLANE -elseif awacs then -attribute=WAREHOUSE.Attribute.AIR_AWACS -elseif fighter then -attribute=WAREHOUSE.Attribute.AIR_FIGHTER -elseif bomber then -attribute=WAREHOUSE.Attribute.AIR_BOMBER -elseif tanker then -attribute=WAREHOUSE.Attribute.AIR_TANKER -elseif transporthelo then -attribute=WAREHOUSE.Attribute.AIR_TRANSPORTHELO -elseif attackhelicopter then -attribute=WAREHOUSE.Attribute.AIR_ATTACKHELO -elseif uav then -attribute=WAREHOUSE.Attribute.AIR_UAV -elseif apc then -attribute=WAREHOUSE.Attribute.GROUND_APC -elseif infantry then -attribute=WAREHOUSE.Attribute.GROUND_INFANTRY -elseif artillery then -attribute=WAREHOUSE.Attribute.GROUND_ARTILLERY -elseif tank then -attribute=WAREHOUSE.Attribute.GROUND_TANK -elseif aaa then -attribute=WAREHOUSE.Attribute.GROUND_AAA -elseif ewr then -attribute=WAREHOUSE.Attribute.GROUND_EWR -elseif sam then -attribute=WAREHOUSE.Attribute.GROUND_SAM -elseif truck then -attribute=WAREHOUSE.Attribute.GROUND_TRUCK -elseif train then -attribute=WAREHOUSE.Attribute.GROUND_TRAIN -elseif aircraftcarrier then -attribute=WAREHOUSE.Attribute.NAVAL_AIRCRAFTCARRIER -elseif warship then -attribute=WAREHOUSE.Attribute.NAVAL_WARSHIP -elseif armedship then -attribute=WAREHOUSE.Attribute.NAVAL_ARMEDSHIP -elseif unarmedship then -attribute=WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP -else -if group:IsGround()then -attribute=WAREHOUSE.Attribute.GROUND_OTHER -elseif group:IsShip()then -attribute=WAREHOUSE.Attribute.NAVAL_OTHER -elseif group:IsAir()then -attribute=WAREHOUSE.Attribute.AIR_OTHER -else -attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN -end -end -end -return attribute -end -function WAREHOUSE:_GetObjectSize(DCSobject) -local DCSdesc=DCSobject:getDesc() -if DCSdesc.box then -local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) -local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) -local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) -return math.max(x,z),x,y,z -end -return 0,0,0,0 -end -function WAREHOUSE:GetStockInfo(stock) -local _data={} -for _j,_attribute in pairs(WAREHOUSE.Attribute)do -local n=0 -for _i,_item in pairs(stock)do -local _ite=_item -if _ite.attribute==_attribute then -n=n+1 -end -end -_data[_attribute]=n -end -return _data -end -function WAREHOUSE:_DeleteStockItem(stockitem) -for i=1,#self.stock do -local item=self.stock[i] -if item.uid==stockitem.uid then -table.remove(self.stock,i) -break -end -end -end -function WAREHOUSE:_DeleteQueueItem(qitem,queue) -self:F({qitem=qitem,queue=queue}) -for i=1,#queue do -local _item=queue[i] -if _item.uid==qitem.uid then -self:T(self.lid..string.format("Deleting queue item id=%d.",qitem.uid)) -table.remove(queue,i) -break -end -end -end -function WAREHOUSE:_DeleteQueueItemByID(qitemID,queue) -for i=1,#queue do -local _item=queue[i] -if _item.uid==qitemID then -self:T(self.lid..string.format("Deleting queue item id=%d.",qitemID)) -table.remove(queue,i) -break -end -end -end -function WAREHOUSE:_SortQueue() -self:F3() -local function _sort(a,b) -return(a.prio=2 then -local total="Empty" -if#queue>0 then -total=string.format("Total = %d",#queue) -end -local text=string.format("%s at %s: %s",name,self.alias,total) -for i,qitem in ipairs(queue)do -local qitem=qitem -local uid=qitem.uid -local prio=qitem.prio -local clock="N/A" -if qitem.timestamp then -clock=tostring(UTILS.SecondsToClock(qitem.timestamp)) -end -local assignment=tostring(qitem.assignment) -local requestor=qitem.warehouse.alias -local airbasename=qitem.warehouse:GetAirbaseName() -local requestorAirbaseCat=qitem.warehouse:GetAirbaseCategory() -local assetdesc=qitem.assetdesc -local assetdescval=qitem.assetdescval -if assetdesc==WAREHOUSE.Descriptor.ASSETLIST then -assetdescval="Asset list" -end -local nasset=tostring(qitem.nasset) -local ndelivered=tostring(qitem.ndelivered) -local ncargogroupset="N/A" -if qitem.cargogroupset then -ncargogroupset=tostring(qitem.cargogroupset:Count()) -end -local transporttype="N/A" -if qitem.transporttype then -transporttype=qitem.transporttype -end -local ntransport="N/A" -if qitem.ntransport then -ntransport=tostring(qitem.ntransport) -end -local ntransportalive="N/A" -if qitem.transportgroupset then -ntransportalive=tostring(qitem.transportgroupset:Count()) -end -local ntransporthome="N/A" -if qitem.ntransporthome then -ntransporthome=tostring(qitem.ntransporthome) -end -text=text..string.format( -"\n%d) UID=%d, Prio=%d, Clock=%s, Assignment=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%s / #alive=%s / #home=%s", -i,uid,prio,clock,assignment,requestor,airbasename,requestorAirbaseCat,assetdesc,assetdescval,nasset,ncargogroupset,ndelivered,transporttype,ntransport,ntransportalive,ntransporthome) -end -if#queue==0 then -self:I(self.lid..text) -else -if total~="Empty"then -self:I(self.lid..text) -end -end -end -end -function WAREHOUSE:_DisplayStatus() -if self.verbosity>=3 then -local text=string.format("\n------------------------------------------------------\n") -text=text..string.format("Warehouse %s status: %s\n",self.alias,self:GetState()) -text=text..string.format("------------------------------------------------------\n") -text=text..string.format("Coalition name = %s\n",self:GetCoalitionName()) -text=text..string.format("Country name = %s\n",self:GetCountryName()) -text=text..string.format("Airbase name = %s (category=%d)\n",self:GetAirbaseName(),self:GetAirbaseCategory()) -text=text..string.format("Queued requests = %d\n",#self.queue) -text=text..string.format("Pending requests = %d\n",#self.pending) -text=text..string.format("------------------------------------------------------\n") -text=text..self:_GetStockAssetsText() -self:I(text) -end -end -function WAREHOUSE:_GetStockAssetsText(messagetoall) -local _data=self:GetStockInfo(self.stock) -local text="Stock:\n" -local total=0 -for _attribute,_count in pairs(_data)do -if _count>0 then -local attribute=tostring(UTILS.Split(_attribute,"_")[2]) -text=text..string.format("%s = %d\n",attribute,_count) -total=total+_count -end -end -text=text..string.format("===================\n") -text=text..string.format("Total = %d\n",total) -text=text..string.format("------------------------------------------------------\n") -MESSAGE:New(text,10):ToAllIf(messagetoall) -return text -end -function WAREHOUSE:_UpdateWarehouseMarkText() -if self.markerOn then -local text=string.format("Warehouse state: %s\nTotal assets in stock %d:\n",self:GetState(),#self.stock) -for _attribute,_count in pairs(self:GetStockInfo(self.stock)or{})do -if _count>0 then -local attribute=tostring(UTILS.Split(_attribute,"_")[2]) -text=text..string.format("%s=%d, ",attribute,_count) -end -end -local coordinate=self:GetCoordinate() -local coalition=self:GetCoalition() -if not self.markerWarehouse then -self.markerWarehouse=MARKER:New(coordinate,text):ToCoalition(coalition) -else -local refresh=false -if self.markerWarehouse.text~=text then -self.markerWarehouse.text=text -refresh=true -end -if self.markerWarehouse.coordinate~=coordinate then -self.markerWarehouse.coordinate=coordinate -refresh=true -end -if self.markerWarehouse.coalition~=coalition then -self.markerWarehouse.coalition=coalition -refresh=true -end -if refresh then -self.markerWarehouse:Refresh() -end -end -end -end -function WAREHOUSE:_DisplayStockItems(stock) -local text=self.lid..string.format("Warehouse %s stock assets:",self.alias) -for _i,_stock in pairs(stock)do -local mystock=_stock -local name=mystock.templatename -local category=mystock.category -local cargobaymax=mystock.cargobaymax -local cargobaytot=mystock.cargobaytot -local nunits=mystock.nunits -local range=mystock.range -local size=mystock.size -local speed=mystock.speedmax -local uid=mystock.uid -local unittype=mystock.unittype -local weight=mystock.weight -local attribute=mystock.attribute -text=text..string.format("\n%02d) uid=%d, name=%s, unittype=%s, category=%d, attribute=%s, nunits=%d, speed=%.1f km/h, range=%.1f km, size=%.1f m, weight=%.1f kg, cargobax max=%.1f kg tot=%.1f kg", -_i,uid,name,unittype,category,attribute,nunits,speed,range/1000,size,weight,cargobaymax,cargobaytot) -end -self:T3(text) -end -function WAREHOUSE:_Fireworks(coord) -coord=coord or self:GetCoordinate() -for i=1,91 do -local color=math.random(0,3) -coord:Flare(color,i-1) -end -end -function WAREHOUSE:_InfoMessage(text,duration) -duration=duration or 20 -if duration>0 and self.Debug or self.Report then -MESSAGE:New(text,duration):ToCoalition(self:GetCoalition()) -end -self:I(self.lid..text) -end -function WAREHOUSE:_DebugMessage(text,duration) -duration=duration or 20 -if duration>0 then -MESSAGE:New(text,duration):ToAllIf(self.Debug) -end -self:T(self.lid..text) -end -function WAREHOUSE:_ErrorMessage(text,duration) -duration=duration or 20 -if duration>0 then -MESSAGE:New(text,duration):ToAll() -end -self:E(self.lid..text) -end -function WAREHOUSE:_GetMaxHeight(D,alphaC,alphaD,Hdep,Hdest,Deltahhold) -local Hhold=Hdest+Deltahhold -local hdest=Hdest-Hdep -local hhold=hdest+Deltahhold -local Dp=math.sqrt(D^2+hhold^2) -local alphaS=math.atan(hdest/D) -local alphaH=math.atan(hhold/D) -local alphaCp=alphaC-alphaH -local alphaDp=alphaD+alphaH -local gammap=math.pi-alphaCp-alphaDp -local sCp=Dp*math.sin(alphaDp)/math.sin(gammap) -local sDp=Dp*math.sin(alphaCp)/math.sin(gammap) -local hmax=sCp*math.sin(alphaC) -if self.Debug then -env.info(string.format("Hdep = %.3f km",Hdep/1000)) -env.info(string.format("Hdest = %.3f km",Hdest/1000)) -env.info(string.format("DetaHold= %.3f km",Deltahhold/1000)) -env.info() -env.info(string.format("D = %.3f km",D/1000)) -env.info(string.format("Dp = %.3f km",Dp/1000)) -env.info() -env.info(string.format("alphaC = %.3f Deg",math.deg(alphaC))) -env.info(string.format("alphaCp = %.3f Deg",math.deg(alphaCp))) -env.info() -env.info(string.format("alphaD = %.3f Deg",math.deg(alphaD))) -env.info(string.format("alphaDp = %.3f Deg",math.deg(alphaDp))) -env.info() -env.info(string.format("alphaS = %.3f Deg",math.deg(alphaS))) -env.info(string.format("alphaH = %.3f Deg",math.deg(alphaH))) -env.info() -env.info(string.format("sCp = %.3f km",sCp/1000)) -env.info(string.format("sDp = %.3f km",sDp/1000)) -env.info() -env.info(string.format("hmax = %.3f km",hmax/1000)) -env.info() -local hdescent=hmax-hhold -local dClimb=hmax/math.tan(alphaC) -local dDescent=(hmax-hhold)/math.tan(alphaD) -local dCruise=D-dClimb-dDescent -env.info(string.format("hmax = %.3f km",hmax/1000)) -env.info(string.format("hdescent = %.3f km",hdescent/1000)) -env.info(string.format("Dclimb = %.3f km",dClimb/1000)) -env.info(string.format("Dcruise = %.3f km",dCruise/1000)) -env.info(string.format("Ddescent = %.3f km",dDescent/1000)) -env.info() -end -return hmax -end -function WAREHOUSE:_GetFlightplan(asset,departure,destination) -local Vmax=asset.speedmax/3.6 -local Range=asset.range -local category=asset.category -local ceiling=asset.DCSdesc.Hmax -local Vymax=asset.DCSdesc.VyMax -local VxCruiseMax=0.90*Vmax -local VxCruiseMin=math.min(VxCruiseMax*0.70,166) -local VxCruise=UTILS.RandomGaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin,(VxCruiseMax-VxCruiseMax)/4,VxCruiseMin,VxCruiseMax) -local VxClimb=math.min(Vmax*0.90,200) -local VxDescent=math.min(Vmax*0.60,140) -local VxHolding=VxDescent*0.9 -local VxFinal=VxHolding*0.9 -local VyClimb=math.min(7.6,Vymax) -local AlphaClimb=math.rad(4) -local AlphaDescent=math.rad(4) -local FLcruise_expect=150*RAT.unit.FL2m -if category==Group.Category.HELICOPTER then -FLcruise_expect=1000 -end -local Pdeparture=departure:GetCoordinate() -local H_departure=Pdeparture.y -local Pdestination=destination:GetCoordinate() -local H_destination=Pdestination.y -local Rhmin=5000 -local Rhmax=10000 -if category==Group.Category.HELICOPTER then -Rhmin=500 -Rhmax=1000 -end -local Pholding=Pdestination:GetRandomCoordinateInRadius(Rhmax,Rhmin) -local d_holding=Pholding:Get2DDistance(Pdestination) -local H_holding=Pholding.y -local heading=Pdeparture:HeadingTo(Pholding) -local d_total=Pdeparture:Get2DDistance(Pholding) -local h_holding=1200 -if category==Group.Category.HELICOPTER then -h_holding=150 -end -h_holding=UTILS.Randomize(h_holding,0.2) -local DeltaholdingMax=self:_GetMaxHeight(d_total,AlphaClimb,AlphaDescent,H_departure,H_holding,0) -if h_holding>DeltaholdingMax then -h_holding=math.abs(DeltaholdingMax) -end -local Hh_holding=H_holding+h_holding -local h_max=self:_GetMaxHeight(d_total,AlphaClimb,AlphaDescent,H_departure,H_holding,h_holding) -local FLmax=h_max+H_departure -local FLmin=math.max(H_departure,Hh_holding) -FLmax=math.min(FLmax,ceiling) -if FLmin>FLmax then -FLmin=FLmax -end -if FLcruise_expectFLmax then -FLcruise_expect=FLmax -end -local FLcruise=UTILS.RandomGaussian(FLcruise_expect,math.abs(FLmax-FLmin)/4,FLmin,FLmax) -local h_climb=FLcruise-H_departure -local h_descent=FLcruise-Hh_holding -local d_climb=h_climb/math.tan(AlphaClimb) -local d_descent=h_descent/math.tan(AlphaDescent) -local d_cruise=d_total-d_climb-d_descent -local text=string.format("Flight plan:\n") -text=text..string.format("Vx max = %.2f km/h\n",Vmax*3.6) -text=text..string.format("Vx climb = %.2f km/h\n",VxClimb*3.6) -text=text..string.format("Vx cruise = %.2f km/h\n",VxCruise*3.6) -text=text..string.format("Vx descent = %.2f km/h\n",VxDescent*3.6) -text=text..string.format("Vx holding = %.2f km/h\n",VxHolding*3.6) -text=text..string.format("Vx final = %.2f km/h\n",VxFinal*3.6) -text=text..string.format("Vy max = %.2f m/s\n",Vymax) -text=text..string.format("Vy climb = %.2f m/s\n",VyClimb) -text=text..string.format("Alpha Climb = %.2f Deg\n",math.deg(AlphaClimb)) -text=text..string.format("Alpha Descent = %.2f Deg\n",math.deg(AlphaDescent)) -text=text..string.format("Dist climb = %.3f km\n",d_climb/1000) -text=text..string.format("Dist cruise = %.3f km\n",d_cruise/1000) -text=text..string.format("Dist descent = %.3f km\n",d_descent/1000) -text=text..string.format("Dist total = %.3f km\n",d_total/1000) -text=text..string.format("h_climb = %.3f km\n",h_climb/1000) -text=text..string.format("h_desc = %.3f km\n",h_descent/1000) -text=text..string.format("h_holding = %.3f km\n",h_holding/1000) -text=text..string.format("h_max = %.3f km\n",h_max/1000) -text=text..string.format("FL min = %.3f km\n",FLmin/1000) -text=text..string.format("FL expect = %.3f km\n",FLcruise_expect/1000) -text=text..string.format("FL cruise * = %.3f km\n",FLcruise/1000) -text=text..string.format("FL max = %.3f km\n",FLmax/1000) -text=text..string.format("Ceiling = %.3f km\n",ceiling/1000) -text=text..string.format("Max range = %.3f km\n",Range/1000) -self:T(self.lid..text) -if d_cruise<0 then -d_cruise=100 -end -local wp={} -local c={} -c[#c+1]=Pdeparture -wp[#wp+1]=Pdeparture:WaypointAir("RADIO",COORDINATE.WaypointType.TakeOffParking,COORDINATE.WaypointAction.FromParkingArea,VxClimb*3.6,true,departure,nil,"Departure") -local Pcruise=Pdeparture:Translate(d_climb,heading) -Pcruise.y=FLcruise -c[#c+1]=Pcruise -wp[#wp+1]=Pcruise:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,VxCruise*3.6,true,nil,nil,"Cruise") -local Pdescent=Pcruise:Translate(d_cruise,heading) -Pdescent.y=FLcruise -c[#c+1]=Pdescent -wp[#wp+1]=Pdescent:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,VxDescent*3.6,true,nil,nil,"Descent") -Pholding.y=H_holding+h_holding -c[#c+1]=Pholding -wp[#wp+1]=Pholding:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,VxHolding*3.6,true,nil,nil,"Holding") -c[#c+1]=Pdestination -wp[#wp+1]=Pdestination:WaypointAir("RADIO",COORDINATE.WaypointType.Land,COORDINATE.WaypointAction.Landing,VxFinal*3.6,true,destination,nil,"Final Destination") -if self.Debug then -for i,coord in pairs(c)do -local coord=coord -local dist=0 -if i>1 then -dist=coord:Get2DDistance(c[i-1]) -end -coord:MarkToAll(string.format("Waypoint %i, distance = %.2f km",i,dist/1000)) -end -end -return wp,c -end -FOX={ -ClassName="FOX", -Debug=false, -lid=nil, -menuadded={}, -menudisabled=nil, -destroy=nil, -launchalert=nil, -marklaunch=nil, -missiles={}, -players={}, -safezones={}, -launchzones={}, -protectedset=nil, -explosionpower=0.1, -explosiondist=200, -explosiondist2=500, -bigmissilemass=50, -destroy=nil, -dt50=5, -dt10=1, -dt05=0.5, -dt01=0.1, -dt00=0.01, -} -FOX.MenuF10={} -FOX.MenuF10Root=nil -FOX.version="0.6.1" -function FOX:New() -self.lid="FOX | " -local self=BASE:Inherit(self,FSM:New()) -self:SetDefaultMissileDestruction(true) -self:SetDefaultLaunchAlerts(true) -self:SetDefaultLaunchMarks(true) -self:SetExplosionDistance() -self:SetExplosionDistanceBigMissiles() -self:SetExplosionPower() -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Running") -self:AddTransition("*","Status","*") -self:AddTransition("*","MissileLaunch","*") -self:AddTransition("*","MissileDestroyed","*") -self:AddTransition("*","EnterSafeZone","*") -self:AddTransition("*","ExitSafeZone","*") -self:AddTransition("Running","Stop","Stopped") -return self -end -function FOX:onafterStart(From,Event,To) -local text=string.format("Starting FOX Missile Trainer %s",FOX.version) -env.info(text) -self:HandleEvent(EVENTS.Birth) -self:HandleEvent(EVENTS.Shot) -if self.Debug then -self:HandleEvent(EVENTS.Hit) -end -if self.Debug then -self:TraceClass(self.ClassName) -self:TraceLevel(2) -end -self:__Status(-20) -end -function FOX:onafterStop(From,Event,To) -local text=string.format("Stopping FOX Missile Trainer %s",FOX.version) -env.info(text) -self:UnHandleEvent(EVENTS.Birth) -self:UnHandleEvent(EVENTS.Shot) -if self.Debug then -self:UnhandleEvent(EVENTS.Hit) -end -end -function FOX:AddSafeZone(zone) -table.insert(self.safezones,zone) -return self -end -function FOX:AddLaunchZone(zone) -table.insert(self.launchzones,zone) -return self -end -function FOX:SetProtectedGroupSet(groupset) -self.protectedset=groupset -return self -end -function FOX:AddProtectedGroup(group) -if not self.protectedset then -self.protectedset=SET_GROUP:New() -end -self.protectedset:AddGroup(group) -return self -end -function FOX:SetExplosionPower(power) -self.explosionpower=power or 0.1 -return self -end -function FOX:SetExplosionDistance(distance) -self.explosiondist=distance or 200 -return self -end -function FOX:SetExplosionDistanceBigMissiles(distance,explosivemass) -self.explosiondist2=distance or 500 -self.bigmissilemass=explosivemass or 50 -return self -end -function FOX:SetDisableF10Menu() -self.menudisabled=true -return self -end -function FOX:SetDefaultMissileDestruction(switch) -if switch==nil then -self.destroy=false -else -self.destroy=switch -end -return self -end -function FOX:SetDefaultLaunchAlerts(switch) -if switch==nil then -self.launchalert=false -else -self.launchalert=switch -end -return self -end -function FOX:SetDefaultLaunchMarks(switch) -if switch==nil then -self.marklaunch=false -else -self.marklaunch=switch -end -return self -end -function FOX:SetDebugOnOff(switch) -if switch==nil then -self.Debug=false -else -self.Debug=switch -end -return self -end -function FOX:SetDebugOn() -self:SetDebugOnOff(true) -return self -end -function FOX:SetDebugOff() -self:SetDebugOff(false) -return self -end -function FOX:onafterStatus(From,Event,To) -local fsmstate=self:GetState() -local time=timer.getAbsTime() -local clock=UTILS.SecondsToClock(time) -self:I(self.lid..string.format("Missile trainer status %s: %s",clock,fsmstate)) -self:_CheckMissileStatus() -self:_CheckPlayers() -if fsmstate=="Running"then -self:__Status(-10) -end -end -function FOX:_CheckPlayers() -for playername,_playersettings in pairs(self.players)do -local playersettings=_playersettings -local unitname=playersettings.unitname -local unit=UNIT:FindByName(unitname) -if unit and unit:IsAlive()then -local coord=unit:GetCoordinate() -local issafe=self:_CheckCoordSafe(coord) -if issafe then -if not playersettings.inzone then -self:EnterSafeZone(playersettings) -playersettings.inzone=true -end -else -if playersettings.inzone==true then -self:ExitSafeZone(playersettings) -playersettings.inzone=false -end -end -end -end -end -function FOX:_RemoveMissile(missile) -if missile then -for i,_missile in pairs(self.missiles)do -local m=_missile -if missile.missileName==m.missileName then -table.remove(self.missiles,i) -return -end -end -end -end -function FOX:_CheckMissileStatus() -local text="Missiles:" -local inactive={} -for i,_missile in pairs(self.missiles)do -local missile=_missile -local targetname="unkown" -if missile.targetUnit then -targetname=missile.targetUnit:GetName() -end -local playername="none" -if missile.targetPlayer then -playername=missile.targetPlayer.name -end -local active=tostring(missile.active) -local mtype=missile.missileType -local dtype=missile.missileType -local range=UTILS.MetersToNM(missile.missileRange) -if not active then -table.insert(inactive,i) -end -local heading=self:_GetWeapongHeading(missile.weapon) -text=text..string.format("\n[%d] %s: active=%s, range=%.1f NM, heading=%03d, target=%s, player=%s, missilename=%s",i,mtype,active,range,heading,targetname,playername,missile.missileName) -end -if#self.missiles==0 then -text=text.." none" -end -self:I(self.lid..text) -for i=#self.missiles,1,-1 do -local missile=self.missiles[i] -if missile and not missile.active then -table.remove(self.missiles,i) -end -end -end -function FOX:_IsProtected(targetunit) -if not self.protectedset then -return false -end -if targetunit and targetunit:IsAlive()then -local targetgroup=targetunit:GetGroup() -if targetgroup then -local targetname=targetgroup:GetName() -for _,_group in pairs(self.protectedset:GetSetObjects())do -local group=_group -if group then -local groupname=group:GetName() -if targetname==groupname then -return true -end -end -end -end -end -return false -end -function FOX:onafterMissileLaunch(From,Event,To,missile) -local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s",missile.missileType,missile.missileName,tostring(missile.targetName),missile.shooterName) -self:I(FOX.lid..text) -MESSAGE:New(text,10):ToAllIf(self.Debug) -for _,_player in pairs(self.players)do -local player=_player -local playerUnit=player.unit -if playerUnit and playerUnit:IsAlive()and player.coalition~=missile.shooterCoalition then -local distance=playerUnit:GetCoordinate():Get3DDistance(missile.shotCoord) -local bearing=playerUnit:GetCoordinate():HeadingTo(missile.shotCoord) -if player.launchalert then -if(missile.targetPlayer and player.unitname==missile.targetPlayer.unitname)or(distance=self.bigmissilemass -end -if destroymissile and self:_CheckCoordSafe(targetCoord)then -self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m", -missile.missileType,missile.missileName,missile.shooterName,target:GetName(),tostring(missile.targetPlayer~=nil),distance)) -_ordnance:destroy() -missile.active=false -if self.Debug then -missileCoord:SmokeRed() -targetCoord:SmokeGreen() -end -self:MissileDestroyed(missile) -if self.explosionpower>0 and distance>50 and(distShooter==nil or(distShooter and distShooter>50))then -missileCoord:Explosion(self.explosionpower) -end -if missile.targetPlayer then -local text=string.format("Destroying missile. %s",self:_DeadText()) -MESSAGE:New(text,10):ToGroup(target:GetGroup()) -missile.targetPlayer.dead=missile.targetPlayer.dead+1 -end -return nil -else -local dt=1.0 -if distance>50000 then -dt=self.dt50 -elseif distance>10000 then -dt=self.dt10 -elseif distance>5000 then -dt=self.dt05 -elseif distance>1000 then -dt=self.dt01 -else -dt=self.dt00 -end -return timer.getTime()+dt -end -else -self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.",missile.missileType,missile.missileName,missile.shooterName)) -return timer.getTime()+0.1 -end -else -if target then -local player=self:_GetPlayerFromUnit(target) -if player and player.unit:IsAlive()then -local text=string.format("Missile defeated. Well done, %s!",player.name) -MESSAGE:New(text,10):ToClient(player.client) -player.defeated=player.defeated+1 -end -end -missile.active=false -self:T(FOX.lid..string.format("Terminating missile track timer.")) -return nil -end -end -self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds.")) -timer.scheduleFunction(trackMissile,missile.weapon,timer.getTime()+0.0001) -end -function FOX:OnEventBirth(EventData) -self:F3({eventbirth=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event BIRTH!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event BIRTH!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local playerunit,playername=self:_GetPlayerUnitAndName(_unitName) -self:T(self.lid.."BIRTH: unit = "..tostring(EventData.IniUnitName)) -self:T(self.lid.."BIRTH: group = "..tostring(EventData.IniGroupName)) -self:T(self.lid.."BIRTH: player = "..tostring(playername)) -if playerunit and playername then -local _uid=playerunit:GetID() -local _group=playerunit:GetGroup() -local _callsign=playerunit:GetCallsign() -local text=string.format("Pilot %s, callsign %s entered unit %s of group %s.",playername,_callsign,_unitName,_group:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,5):ToAllIf(self.Debug) -if not self.menudisabled then -SCHEDULER:New(nil,self._AddF10Commands,{self,_unitName},0.1) -end -local playerData={} -playerData.unit=playerunit -playerData.unitname=_unitName -playerData.group=_group -playerData.groupname=_group:GetName() -playerData.name=playername -playerData.callsign=playerData.unit:GetCallsign() -playerData.client=CLIENT:FindByName(_unitName,nil,true) -playerData.coalition=_group:GetCoalition() -playerData.destroy=playerData.destroy or self.destroy -playerData.launchalert=playerData.launchalert or self.launchalert -playerData.marklaunch=playerData.marklaunch or self.marklaunch -playerData.defeated=playerData.defeated or 0 -playerData.dead=playerData.dead or 0 -self.players[playername]=playerData -end -end -function FOX:GetMissileTarget(missile) -local target=nil -local targetName="unknown" -local targetUnit=nil -if missile.weapon and missile.weapon:isExist()then -target=missile.weapon:getTarget() -if target then -self:T2({missiletarget=target}) -targetUnit=UNIT:Find(target) -if targetUnit then -targetName=targetUnit:GetName() -missile.targetUnit=targetUnit -missile.targetPlayer=self:_GetPlayerFromUnit(missile.targetUnit) -end -end -end -if missile.targetName and missile.targetName~=targetName then -self:I(self.lid..string.format("Missile %s(%s) changed target to %s. Previous target was %s.",missile.missileType,missile.missileName,targetName,missile.targetName)) -end -missile.targetName=targetName -end -function FOX:OnEventShot(EventData) -self:T2({eventshot=EventData}) -if EventData.Weapon==nil then -return -end -if EventData.IniDCSUnit==nil then -return -end -local _weapon=EventData.WeaponName -local _target=EventData.Weapon:getTarget() -local _targetName="unknown" -local _targetUnit=nil -local desc=EventData.Weapon:getDesc() -self:T2({desc=desc}) -local weaponcategory=desc.category -local missilecategory=desc.missileCategory -local missilerange=nil -if missilecategory then -missilerange=desc.rangeMaxAltMax -end -self:T2(FOX.lid.."EVENT SHOT: FOX") -self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s",tostring(EventData.IniUnitName))) -self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s",tostring(EventData.IniGroupName))) -self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s",tostring(_weapon))) -self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s",tostring(weaponcategory))) -self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s",tostring(missilecategory))) -self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s",tostring(missilerange))) -if not self:_CheckCoordLaunch(EventData.IniUnit:GetCoordinate())then -self:T(self.lid.."Missile was not fired in launch zone. No tracking!") -return -end -local _track=weaponcategory==1 and missilecategory and(missilecategory==1 or missilecategory==2 or missilecategory==6) -if _track then -local missile={} -missile.active=true -missile.weapon=EventData.weapon -missile.missileType=_weapon -missile.missileRange=missilerange -missile.missileName=EventData.weapon:getName() -missile.shooterUnit=EventData.IniUnit -missile.shooterGroup=EventData.IniGroup -missile.shooterCoalition=EventData.IniUnit:GetCoalition() -missile.shooterName=EventData.IniUnitName -missile.shotTime=timer.getAbsTime() -missile.shotCoord=EventData.IniUnit:GetCoordinate() -missile.fuseDist=desc.fuseDist -missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass -missile.targetOrig=missile.targetName -self:GetMissileTarget(missile) -self:I(FOX.lid..string.format("EVENT SHOT: Shooter=%s %s(%s) ==> Target=%s, fuse dist=%s, explosive=%s", -tostring(missile.shooterName),tostring(missile.missileType),tostring(missile.missileName),tostring(missile.targetName),tostring(missile.fuseDist),tostring(missile.explosive))) -if missile.targetPlayer or self:_IsProtected(missile.targetUnit)or missile.targetName=="unknown"then -table.insert(self.missiles,missile) -self:__MissileLaunch(0.1,missile) -end -end -end -function FOX:OnEventHit(EventData) -self:T({eventhit=EventData}) -if EventData.Weapon==nil then -return -end -if EventData.IniUnit==nil then -return -end -if EventData.TgtUnit==nil then -return -end -local weapon=EventData.Weapon -local weaponname=weapon:getName() -for i,_missile in pairs(self.missiles)do -local missile=_missile -if missile.missileName==weaponname then -self:I(self.lid..string.format("WARNING: Missile %s (%s) hit target %s. Missile trainer target was %s.",missile.missileType,missile.missileName,EventData.TgtUnitName,missile.targetName)) -self:I({missile=missile}) -return -end -end -end -function FOX:_AddF10Commands(_unitName) -self:F(_unitName) -local _unit,playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and playername then -local group=_unit:GetGroup() -local gid=group:GetID() -if group and gid then -if not self.menuadded[gid]then -self.menuadded[gid]=true -local _rootPath=nil -if FOX.MenuF10Root then -_rootPath=FOX.MenuF10Root -else -if FOX.MenuF10[gid]==nil then -FOX.MenuF10[gid]=missionCommands.addSubMenuForGroup(gid,"FOX") -end -_rootPath=FOX.MenuF10[gid] -end -missionCommands.addCommandForGroup(gid,"Destroy Missiles On/Off",_rootPath,self._ToggleDestroyMissiles,self,_unitName) -missionCommands.addCommandForGroup(gid,"Launch Alerts On/Off",_rootPath,self._ToggleLaunchAlert,self,_unitName) -missionCommands.addCommandForGroup(gid,"Mark Launch On/Off",_rootPath,self._ToggleLaunchMark,self,_unitName) -missionCommands.addCommandForGroup(gid,"My Status",_rootPath,self._MyStatus,self,_unitName) -end -else -self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.",_unitName)) -end -else -self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.",_unitName)) -end -end -function FOX:_MyStatus(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -local m,mtext=self:_GetTargetMissiles(playerData.name) -local text=string.format("Status of player %s:\n",playerData.name) -local safe=self:_CheckCoordSafe(playerData.unit:GetCoordinate()) -text=text..string.format("Destroy missiles? %s\n",tostring(playerData.destroy)) -text=text..string.format("Launch alert? %s\n",tostring(playerData.launchalert)) -text=text..string.format("Launch marks? %s\n",tostring(playerData.marklaunch)) -text=text..string.format("Am I safe? %s\n",tostring(safe)) -text=text..string.format("Missiles defeated: %d\n",playerData.defeated) -text=text..string.format("Missiles destroyed: %d\n",playerData.dead) -text=text..string.format("Me target: %d\n%s",m,mtext) -MESSAGE:New(text,10,nil,true):ToClient(playerData.client) -end -end -end -function FOX:_GetTargetMissiles(playername) -local text="" -local n=0 -for _,_missile in pairs(self.missiles)do -local missile=_missile -if missile.targetPlayer and missile.targetPlayer.name==playername then -n=n+1 -text=text..string.format("Type %s: active %s\n",missile.missileType,tostring(missile.active)) -end -end -return n,text -end -function FOX:_ToggleLaunchAlert(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -playerData.launchalert=not playerData.launchalert -local text="" -if playerData.launchalert==true then -text=string.format("%s, missile launch alerts are now ENABLED.",playerData.name) -else -text=string.format("%s, missile launch alerts are now DISABLED.",playerData.name) -end -MESSAGE:New(text,5):ToClient(playerData.client) -end -end -end -function FOX:_ToggleLaunchMark(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -playerData.marklaunch=not playerData.marklaunch -local text="" -if playerData.marklaunch==true then -text=string.format("%s, missile launch marks are now ENABLED.",playerData.name) -else -text=string.format("%s, missile launch marks are now DISABLED.",playerData.name) -end -MESSAGE:New(text,5):ToClient(playerData.client) -end -end -end -function FOX:_ToggleDestroyMissiles(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -playerData.destroy=not playerData.destroy -local text="" -if playerData.destroy==true then -text=string.format("%s, incoming missiles will be DESTROYED.",playerData.name) -else -text=string.format("%s, incoming missiles will NOT be DESTROYED.",playerData.name) -end -MESSAGE:New(text,5):ToClient(playerData.client) -end -end -end -function FOX:_DeadText() -local texts={} -texts[1]="You're dead!" -texts[2]="Meet your maker!" -texts[3]="Time to meet your maker!" -texts[4]="Well, I guess that was it!" -texts[5]="Bye, bye!" -texts[6]="Cheers buddy, was nice knowing you!" -local r=math.random(#texts) -return texts[r] -end -function FOX:_CheckCoordSafe(coord) -if#self.safezones==0 then -return true -end -for _,_zone in pairs(self.safezones)do -local zone=_zone -local inzone=zone:IsCoordinateInZone(coord) -if inzone then -return true -end -end -return false -end -function FOX:_CheckCoordLaunch(coord) -if#self.launchzones==0 then -return true -end -for _,_zone in pairs(self.launchzones)do -local zone=_zone -local inzone=zone:IsCoordinateInZone(coord) -if inzone then -return true -end -end -return false -end -function FOX:_GetWeapongHeading(weapon) -if weapon and weapon:isExist()then -local wp=weapon:getPosition() -local wph=math.atan2(wp.x.z,wp.x.x) -if wph<0 then -wph=wph+2*math.pi -end -wph=math.deg(wph) -return wph -end -return-1 -end -function FOX:_SayNotchingHeadings(playerData,weapon) -if playerData and playerData.unit and playerData.unit:IsAlive()then -local nr,nl=self:_GetNotchingHeadings(weapon) -if nr and nl then -local text=string.format("Notching heading %03d° or %03d°",nr,nl) -MESSAGE:New(text,5,"FOX"):ToClient(playerData.client) -end -end -end -function FOX:_GetNotchingHeadings(weapon) -if weapon then -local hdg=self:_GetWeapongHeading(weapon) -local hdg1=hdg+90 -if hdg1>360 then -hdg1=hdg1-360 -end -local hdg2=hdg-90 -if hdg2<0 then -hdg2=hdg2+360 -end -return hdg1,hdg2 -end -return nil,nil -end -function FOX:_GetPlayerFromUnitname(unitName) -for _,_player in pairs(self.players)do -local player=_player -if player.unitname==unitName then -return player -end -end -return nil -end -function FOX:_GetPlayerFromUnit(unit) -if unit and unit:IsAlive()then -local unitname=unit:GetName() -for _,_player in pairs(self.players)do -local player=_player -if player.unitname==unitname then -return player -end -end -end -return nil -end -function FOX:_GetPlayerUnitAndName(_unitName) -self:F2(_unitName) -if _unitName~=nil then -local DCSunit=Unit.getByName(_unitName) -if DCSunit then -local playername=DCSunit:getPlayerName() -local unit=UNIT:Find(DCSunit) -self:T2({DCSunit=DCSunit,unit=unit,playername=playername}) -if DCSunit and unit and playername then -self:T(self.lid..string.format("Found DCS unit %s with player %s.",tostring(_unitName),tostring(playername))) -return unit,playername -end -end -end -return nil,nil -end -MANTIS={ -ClassName="MANTIS", -name="mymantis", -SAM_Templates_Prefix="", -SAM_Group=nil, -EWR_Templates_Prefix="", -EWR_Group=nil, -Adv_EWR_Group=nil, -HQ_Template_CC="", -HQ_CC=nil, -SAM_Table={}, -lid="", -Detection=nil, -AWACS_Detection=nil, -debug=false, -checkradius=25000, -grouping=5000, -acceptrange=80000, -detectinterval=30, -engagerange=75, -autorelocate=false, -advanced=false, -adv_ratio=100, -adv_state=0, -AWACS_Prefix="", -advAwacs=false, -verbose=false, -awacsrange=250000, -Shorad=nil, -ShoradLink=false, -ShoradTime=600, -ShoradActDistance=15000, -UseEmOnOff=true, -} -do -function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs,EmOnOff) -self.name=name or"mymantis" -self.SAM_Templates_Prefix=samprefix or"Red SAM" -self.EWR_Templates_Prefix=ewrprefix or"Red EWR" -self.HQ_Template_CC=hq or nil -self.Coalition=coaltion or"red" -self.SAM_Table={} -self.dynamic=dynamic or false -self.checkradius=25000 -self.grouping=5000 -self.acceptrange=80000 -self.detectinterval=30 -self.engagerange=75 -self.autorelocate=false -self.autorelocateunits={HQ=false,EWR=false} -self.advanced=false -self.adv_ratio=100 -self.adv_state=0 -self.verbose=false -self.Adv_EWR_Group=nil -self.AWACS_Prefix=awacs or nil -self.awacsrange=250000 -self.Shorad=nil -self.ShoradLink=false -self.ShoradTime=600 -self.ShoradActDistance=15000 -if EmOnOff then -if EmOnOff==false then -self.UseEmOnOff=false -end -end -if type(awacs)=="string"then -self.advAwacs=true -else -self.advAwacs=false -end -local self=BASE:Inherit(self,BASE:New()) -self.lid=string.format("MANTIS %s | ",self.name) -if self.debug then -BASE:TraceOnOff(true) -BASE:TraceClass(self.ClassName) -BASE:TraceLevel(1) -end -if self.dynamic then -self.SAM_Group=SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart() -self.EWR_Group=SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterStart() -else -self.SAM_Group=SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce() -self.EWR_Group=SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterOnce() -end -if self.HQ_Template_CC then -self.HQ_CC=GROUP:FindByName(self.HQ_Template_CC) -end -self.version="0.4.1" -self:I(string.format("***** Starting MANTIS Version %s *****",self.version)) -return self -end -function MANTIS:_GetSAMTable() -return self.SAM_Table -end -function MANTIS:_SetSAMTable(table) -self.SAM_Table=table -return self -end -function MANTIS:SetEWRGrouping(radius) -local radius=radius or 5000 -self.grouping=radius -end -function MANTIS:SetEWRRange(radius) -local radius=radius or 80000 -self.acceptrange=radius -end -function MANTIS:SetSAMRadius(radius) -local radius=radius or 25000 -self.checkradius=radius -end -function MANTIS:SetSAMRange(range) -local range=range or 75 -if range<0 or range>100 then -range=75 -end -self.engagerange=range -end -function MANTIS:SetNewSAMRangeWhileRunning(range) -local range=range or 75 -if range<0 or range>100 then -range=75 -end -self.engagerange=range -self:_RefreshSAMTable() -self.mysead.EngagementRange=range -end -function MANTIS:Debug(onoff) -local onoff=onoff or false -self.debug=onoff -end -function MANTIS:GetCommandCenter() -if self.HQ_CC then -return self.HQ_CC -else -return nil -end -end -function MANTIS:SetAwacs(prefix) -if prefix~=nil then -if type(prefix)=="string"then -self.AWACS_Prefix=prefix -self.advAwacs=true -end -end -end -function MANTIS:SetAwacsRange(range) -local range=range or 250000 -self.awacsrange=range -end -function MANTIS:SetCommandCenter(group) -local group=group or nil -if group~=nil then -if type(group)=="string"then -self.HQ_CC=GROUP:FindByName(group) -self.HQ_Template_CC=group -else -self.HQ_CC=group -self.HQ_Template_CC=group:GetName() -end -end -end -function MANTIS:SetDetectInterval(interval) -local interval=interval or 30 -self.detectinterval=interval -end -function MANTIS:SetAdvancedMode(onoff,ratio) -self:F({onoff,ratio}) -local onoff=onoff or false -local ratio=ratio or 100 -if(type(self.HQ_Template_CC)=="string")and onoff and self.dynamic then -self.adv_ratio=ratio -self.advanced=true -self.adv_state=0 -self.Adv_EWR_Group=SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart() -env.info(string.format("***** Starting Advanced Mode MANTIS Version %s *****",self.version)) -else -local text=self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both." -local m=MESSAGE:New(text,10,"MANTIS",true):ToAll() -BASE:E(text) -end -end -function MANTIS:SetUsingEmOnOff(switch) -self.UseEmOnOff=switch or false -end -function MANTIS:_CheckHQState() -local text=self.lid.." Checking HQ State" -self:T(text) -local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) -if self.verbose then env.info(text)end -if self.advanced then -local hq=self.HQ_Template_CC -local hqgrp=GROUP:FindByName(hq) -if hqgrp then -if hqgrp:IsAlive()then -env.info(self.lid.." HQ is alive!") -return true -else -env.info(self.lid.." HQ is dead!") -return false -end -end -end -end -function MANTIS:_CheckEWRState() -local text=self.lid.." Checking EWR State" -self:F(text) -local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) -if self.verbose then env.info(text)end -if self.advanced then -local EWR_Group=self.Adv_EWR_Group -local nalive=EWR_Group:CountAlive() -if self.advAwacs then -local awacs=GROUP:FindByName(self.AWACS_Prefix) -if awacs~=nil then -if awacs:IsAlive()then -nalive=nalive+1 -end -end -end -env.info(self.lid..string.format(" No of EWR alive is %d",nalive)) -if nalive>0 then -return true -else -return false -end -end -end -function MANTIS:_CheckAdvState() -local text=self.lid.." Checking Advanced State" -self:F(text) -local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) -if self.verbose then env.info(text)end -local currstate=self.adv_state -local EWR_State=self:_CheckEWRState() -local HQ_State=self:_CheckHQState() -if EWR_State and HQ_State then -self.adv_state=0 -elseif EWR_State or HQ_State then -self.adv_state=1 -else -self.adv_state=2 -end -local interval=self.detectinterval -local ratio=self.adv_ratio/100 -ratio=ratio*self.adv_state -local newinterval=interval+(interval*ratio) -local text=self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d",currstate,self.adv_state,newinterval) -self:F(text) -local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) -if self.verbose then env.info(text)end -return newinterval,currstate -end -function MANTIS:SetAutoRelocate(hq,ewr) -self:F({hq,ewr}) -local hqrel=hq or false -local ewrel=ewr or false -if hqrel or ewrel then -self.autorelocate=true -self.autorelocateunits={HQ=hqrel,EWR=ewrel} -self:T({self.autorelocate,self.autorelocateunits}) -end -end -function MANTIS:_RelocateGroups() -self:T(self.lid.." Relocating Groups") -local text=self.lid.." Relocating Groups" -local m=MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug) -if self.verbose then env.info(text)end -if self.autorelocate then -if self.autorelocateunits.HQ and self.HQ_CC then -local _hqgrp=self.HQ_CC -self:T(self.lid.." Relocating HQ") -local text=self.lid.." Relocating HQ" -local m=MESSAGE:New(text,10,"MANTIS"):ToAll() -_hqgrp:RelocateGroundRandomInRadius(20,500,true,true) -end -if self.autorelocateunits.EWR then -local EWR_GRP=SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce() -local EWR_Grps=EWR_GRP.Set -for _,_grp in pairs(EWR_Grps)do -if _grp:IsGround()then -self:T(self.lid.." Relocating EWR ".._grp:GetName()) -local text=self.lid.." Relocating EWR ".._grp:GetName() -local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) -if self.verbose then env.info(text)end -_grp:RelocateGroundRandomInRadius(20,500,true,true) -end -end -end -end -end -function MANTIS:CheckObjectInZone(dectset,samcoordinate) -self:F(self.lid.."CheckObjectInZone Called") -local radius=self.checkradius -local set=dectset -for _,_coord in pairs(set)do -local coord=_coord -local dectstring=coord:ToStringLLDMS() -local samstring=samcoordinate:ToStringLLDMS() -local targetdistance=samcoordinate:DistanceFromPointVec2(coord) -local text=string.format("Checking SAM at % s - Distance %d m - Target %s",samstring,targetdistance,dectstring) -local m=MESSAGE:New(text,10,"Check"):ToAllIf(self.debug) -if self.verbose then env.info(self.lid..text)end -if targetdistance<=radius then -return true,targetdistance -end -end -return false,0 -end -function MANTIS:StartDetection() -self:F(self.lid.."Starting Detection") -local groupset=self.EWR_Group -local grouping=self.grouping or 5000 -local acceptrange=self.acceptrange or 80000 -local interval=self.detectinterval or 60 -_MANTISdetection=DETECTION_AREAS:New(groupset,grouping) -_MANTISdetection:FilterCategories({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER}) -_MANTISdetection:SetAcceptRange(acceptrange) -_MANTISdetection:SetRefreshTimeInterval(interval) -_MANTISdetection:Start() -function _MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem) -local debug=false -if DetectedItem.IsDetected and debug then -local Coordinate=DetectedItem.Coordinate -local text="MANTIS: Detection at "..Coordinate:ToStringLLDMS() -local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) -end -end -return _MANTISdetection -end -function MANTIS:StartAwacsDetection() -self:F(self.lid.."Starting Awacs Detection") -local group=self.AWACS_Prefix -local groupset=SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart() -local grouping=self.grouping or 5000 -local interval=self.detectinterval or 60 -_MANTISAwacs=DETECTION_AREAS:New(groupset,grouping) -_MANTISAwacs:FilterCategories({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER}) -_MANTISAwacs:SetAcceptRange(self.awacsrange) -_MANTISAwacs:SetRefreshTimeInterval(interval) -_MANTISAwacs:Start() -function _MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem) -local debug=false -if DetectedItem.IsDetected and debug then -local Coordinate=DetectedItem.Coordinate -local text="Awacs Detection at "..Coordinate:ToStringLLDMS() -local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) -end -end -return _MANTISAwacs -end -function MANTIS:SetSAMStartState() -self:F(self.lid.."Setting SAM Start States") -local SAM_SET=self.SAM_Group -local SAM_Grps=SAM_SET.Set -local SAM_Tbl={} -local SEAD_Grps={} -local engagerange=self.engagerange -for _i,_group in pairs(SAM_Grps)do -local group=_group -if self.UseEmOnOff then -group:EnableEmission(false) -else -group:OptionAlarmStateGreen() -end -group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) -if group:IsGround()then -local grpname=group:GetName() -local grpcoord=group:GetCoordinate() -table.insert(SAM_Tbl,{grpname,grpcoord}) -table.insert(SEAD_Grps,grpname) -end -end -self.SAM_Table=SAM_Tbl -local mysead=SEAD:New(SEAD_Grps) -mysead:SetEngagementRange(engagerange) -self.mysead=mysead -return self -end -function MANTIS:_RefreshSAMTable() -self:F(self.lid.."Setting SAM Start States") -local SAM_SET=self.SAM_Group -local SAM_Grps=SAM_SET.Set -local SAM_Tbl={} -local SEAD_Grps={} -local engagerange=self.engagerange -for _i,_group in pairs(SAM_Grps)do -local group=_group -group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) -if group:IsGround()then -local grpname=group:GetName() -local grpcoord=group:GetCoordinate() -table.insert(SAM_Tbl,{grpname,grpcoord}) -table.insert(SEAD_Grps,grpname) -end -end -self.SAM_Table=SAM_Tbl -if self.mysead~=nil then -local mysead=self.mysead -mysead:UpdateSet(SEAD_Grps) -end -return self -end -function MANTIS:AddShorad(Shorad,Shoradtime) -local Shorad=Shorad or nil -local ShoradTime=Shoradtime or 600 -local ShoradLink=true -if Shorad:IsInstanceOf("SHORAD")then -self.ShoradLink=ShoradLink -self.Shorad=Shorad -self.ShoradTime=Shoradtime -end -end -function MANTIS:RemoveShorad() -self.ShoradLink=false -end -function MANTIS:Start() -self:F(self.lid.."Starting MANTIS") -self:SetSAMStartState() -self.Detection=self:StartDetection() -if self.advAwacs then -self.AWACS_Detection=self:StartAwacsDetection() -end -local function check(detection) -local detset=detection:GetDetectedItemCoordinates() -self:F("Check:",{detset}) -local rand=math.random(1,100) -if rand>65 then -self:_RefreshSAMTable() -end -local samset=self:_GetSAMTable() -for _,_data in pairs(samset)do -local samcoordinate=_data[2] -local name=_data[1] -local samgroup=GROUP:FindByName(name) -local IsInZone,Distance=self:CheckObjectInZone(detset,samcoordinate) -if IsInZone then -if samgroup:IsAlive()then -if self.UseEmOnOff then -samgroup:EnableEmission(true) -end -samgroup:OptionAlarmStateRed() -if self.ShoradLink and Distance100)or(low>high)then -low=70 -end -if(high<0)or(high>100)or(highTstop then -self:E(string.format("ERROR: Recovery stop time %s lies before recovery start time %s! Recovery window rejected.",UTILS.SecondsToClock(Tstart),UTILS.SecondsToClock(Tstop))) -return self -end -if Tstop<=Tnow then -self:I(string.format("WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.",UTILS.SecondsToClock(Tstop),UTILS.SecondsToClock(Tnow))) -return self -end -case=case or self.defaultcase -holdingoffset=holdingoffset or self.defaultoffset -if case==1 then -holdingoffset=0 -end -self.windowcount=self.windowcount+1 -local recovery={} -recovery.START=Tstart -recovery.STOP=Tstop -recovery.CASE=case -recovery.OFFSET=holdingoffset -recovery.OPEN=false -recovery.OVER=false -recovery.WIND=turnintowind -recovery.SPEED=speed or 20 -recovery.ID=self.windowcount -if uturn==nil or uturn==true then -recovery.UTURN=true -else -recovery.UTURN=false -end -table.insert(self.recoverytimes,recovery) -return recovery -end -function AIRBOSS:SetSquadronAI(setgroup) -self.squadsetAI=setgroup -return self -end -function AIRBOSS:SetExcludeAI(setgroup) -self.excludesetAI=setgroup -return self -end -function AIRBOSS:AddExcludeAI(group) -self.excludesetAI=self.excludesetAI or SET_GROUP:New() -self.excludesetAI:AddGroup(group) -return self -end -function AIRBOSS:CloseCurrentRecoveryWindow(delay) -if delay and delay>0 then -self:ScheduleOnce(delay,self.CloseCurrentRecoveryWindow,self) -else -if self:IsRecovering()and self.recoverywindow and self.recoverywindow.OPEN then -self:RecoveryStop() -self.recoverywindow.OPEN=false -self.recoverywindow.OVER=true -self:DeleteRecoveryWindow(self.recoverywindow) -end -end -end -function AIRBOSS:DeleteAllRecoveryWindows(delay) -for _,recovery in pairs(self.recoverytimes)do -self:I(self.lid..string.format("Deleting recovery window ID %s",tostring(recovery.ID))) -self:DeleteRecoveryWindow(recovery,delay) -end -return self -end -function AIRBOSS:GetRecoveryWindowByID(id) -if id then -for _,_window in pairs(self.recoverytimes)do -local window=_window -if window and window.ID==id then -return window -end -end -end -return nil -end -function AIRBOSS:DeleteRecoveryWindow(window,delay) -if delay and delay>0 then -self:ScheduleOnce(delay,self.DeleteRecoveryWindow,self,window) -else -for i,_recovery in pairs(self.recoverytimes)do -local recovery=_recovery -if window and window.ID==recovery.ID then -if window.OPEN then -self:RecoveryStop() -else -table.remove(self.recoverytimes,i) -end -end -end -end -end -function AIRBOSS:SetRecoveryTurnTime(interval) -self.dTturn=interval or 300 -return self -end -function AIRBOSS:SetMPWireCorrection(Dcorr) -self.mpWireCorrection=Dcorr or 12 -return self -end -function AIRBOSS:SetQueueUpdateTime(interval) -self.dTqueue=interval or 30 -return self -end -function AIRBOSS:SetLSOCallInterval(timeinterval) -self.LSOdT=timeinterval or 4 -return self -end -function AIRBOSS:SetAirbossNiceGuy(switch) -if switch==true or switch==nil then -self.airbossnice=true -else -self.airbossnice=false -end -return self -end -function AIRBOSS:SetEmergencyLandings(switch) -if switch==true or switch==nil then -self.emergency=true -else -self.emergency=false -end -return self -end -function AIRBOSS:SetDespawnOnEngineShutdown(switch) -if switch==true or switch==nil then -self.despawnshutdown=true -else -self.despawnshutdown=false -end -return self -end -function AIRBOSS:SetRespawnAI(switch) -if switch==true or switch==nil then -self.respawnAI=true -else -self.respawnAI=false -end -return self -end -function AIRBOSS:SetRefuelAI(lowfuelthreshold) -self.lowfuelAI=lowfuelthreshold or 10 -return self -end -function AIRBOSS:SetInitialMaxAlt(altitude) -self.initialmaxalt=UTILS.FeetToMeters(altitude or 1300) -return self -end -function AIRBOSS:SetSoundfilesFolder(folderpath) -if folderpath then -local lastchar=string.sub(folderpath,-1) -if lastchar~="/"then -folderpath=folderpath.."/" -end -end -self.soundfolder=folderpath -self:I(self.lid..string.format("Setting sound files folder to: %s",self.soundfolder)) -return self -end -function AIRBOSS:SetStatusUpdateTime(interval) -self.dTstatus=interval or 0.5 -return self -end -function AIRBOSS:SetDefaultMessageDuration(duration) -self.Tmessage=duration or 10 -return self -end -function AIRBOSS:SetGlideslopeErrorThresholds(_max,_min,High,HIGH,Low,LOW) -self.gle._max=_max or 0.4 -self.gle.High=High or 0.8 -self.gle.HIGH=HIGH or 1.5 -self.gle._min=_min or-0.3 -self.gle.Low=Low or-0.6 -self.gle.LOW=LOW or-0.9 -return self -end -function AIRBOSS:SetLineupErrorThresholds(_max,_min,Left,LeftMed,LEFT,Right,RightMed,RIGHT) -self.lue._max=_max or 0.5 -self.lue._min=_min or-0.5 -self.lue.Left=Left or-1.0 -self.lue.LeftMed=LeftMed or-2.0 -self.lue.LEFT=LEFT or-3.0 -self.lue.Right=Right or 1.0 -self.lue.RightMed=RightMed or 2.0 -self.lue.RIGHT=RIGHT or 3.0 -return self -end -function AIRBOSS:SetMarshalRadius(radius) -self.marshalradius=UTILS.NMToMeters(radius or 2.8) -return self -end -function AIRBOSS:SetMenuSingleCarrier(switch) -if switch==true or switch==nil then -self.menusingle=true -else -self.menusingle=false -end -return self -end -function AIRBOSS:SetMenuMarkZones(switch) -if switch==nil or switch==true then -self.menumarkzones=true -else -self.menumarkzones=false -end -return self -end -function AIRBOSS:SetMenuSmokeZones(switch) -if switch==nil or switch==true then -self.menusmokezones=true -else -self.menusmokezones=false -end -return self -end -function AIRBOSS:SetTrapSheet(path,prefix) -if io then -self.trapsheet=true -self.trappath=path -self.trapprefix=prefix -else -self:E(self.lid.."ERROR: io is not desanitized. Cannot save trap sheet.") -end -return self -end -function AIRBOSS:SetStaticWeather(switch) -if switch==nil or switch==true then -self.staticweather=true -else -self.staticweather=false -end -return self -end -function AIRBOSS:SetTACANoff() -self.TACANon=false -return self -end -function AIRBOSS:SetTACAN(channel,mode,morsecode) -self.TACANchannel=channel or 74 -self.TACANmode=mode or"X" -self.TACANmorse=morsecode or"STN" -self.TACANon=true -return self -end -function AIRBOSS:SetICLSoff() -self.ICLSon=false -return self -end -function AIRBOSS:SetICLS(channel,morsecode) -self.ICLSchannel=channel or 1 -self.ICLSmorse=morsecode or"STN" -self.ICLSon=true -return self -end -function AIRBOSS:SetBeaconRefresh(interval) -self.dTbeacon=interval or 20*60 -return self -end -function AIRBOSS:SetLSORadio(frequency,modulation) -self.LSOFreq=(frequency or 264) -modulation=modulation or"AM" -if modulation=="FM"then -self.LSOModu=radio.modulation.FM -else -self.LSOModu=radio.modulation.AM -end -self.LSORadio={} -self.LSORadio.frequency=self.LSOFreq -self.LSORadio.modulation=self.LSOModu -self.LSORadio.alias="LSO" -return self -end -function AIRBOSS:SetMarshalRadio(frequency,modulation) -self.MarshalFreq=frequency or 305 -modulation=modulation or"AM" -if modulation=="FM"then -self.MarshalModu=radio.modulation.FM -else -self.MarshalModu=radio.modulation.AM -end -self.MarshalRadio={} -self.MarshalRadio.frequency=self.MarshalFreq -self.MarshalRadio.modulation=self.MarshalModu -self.MarshalRadio.alias="MARSHAL" -return self -end -function AIRBOSS:SetRadioUnitName(unitname) -self.senderac=unitname -return self -end -function AIRBOSS:SetRadioRelayLSO(unitname) -self.radiorelayLSO=unitname -return self -end -function AIRBOSS:SetRadioRelayMarshal(unitname) -self.radiorelayMSH=unitname -return self -end -function AIRBOSS:SetUserSoundRadio() -self.usersoundradio=true -return self -end -function AIRBOSS:SoundCheckLSO(delay) -if delay and delay>0 then -self:ScheduleOnce(delay,AIRBOSS.SoundCheckLSO,self) -else -local text="Playing LSO sound files:" -for _name,_call in pairs(self.LSOCall)do -local call=_call -text=text..string.format("\nFile=%s.%s, duration=%.2f sec, loud=%s, subtitle=\"%s\".",call.file,call.suffix,call.duration,tostring(call.loud),call.subtitle) -self:RadioTransmission(self.LSORadio,call,false) -if call.loud then -self:RadioTransmission(self.LSORadio,call,true) -end -end -self:I(self.lid..text) -end -end -function AIRBOSS:SoundCheckMarshal(delay) -if delay and delay>0 then -self:ScheduleOnce(delay,AIRBOSS.SoundCheckMarshal,self) -else -local text="Playing Marshal sound files:" -for _name,_call in pairs(self.MarshalCall)do -local call=_call -text=text..string.format("\nFile=%s.%s, duration=%.2f sec, loud=%s, subtitle=\"%s\".",call.file,call.suffix,call.duration,tostring(call.loud),call.subtitle) -self:RadioTransmission(self.MarshalRadio,call,false) -if call.loud then -self:RadioTransmission(self.MarshalRadio,call,true) -end -end -self:I(self.lid..text) -end -end -function AIRBOSS:SetMaxLandingPattern(nmax) -nmax=nmax or 4 -nmax=math.max(nmax,1) -nmax=math.min(nmax,6) -self.Nmaxpattern=nmax -return self -end -function AIRBOSS:SetMaxMarshalStacks(nmax) -self.Nmaxmarshal=nmax or 3 -self.Nmaxmarshal=math.max(self.Nmaxmarshal,1) -return self -end -function AIRBOSS:SetMaxSectionSize(nmax) -nmax=nmax or 2 -nmax=math.max(nmax,1) -nmax=math.min(nmax,4) -self.NmaxSection=nmax-1 -return self -end -function AIRBOSS:SetMaxFlightsPerStack(nmax) -nmax=nmax or 2 -nmax=math.max(nmax,1) -nmax=math.min(nmax,4) -self.NmaxStack=nmax -return self -end -function AIRBOSS:SetHandleAION() -self.handleai=true -return self -end -function AIRBOSS:SetHandleAIOFF() -self.handleai=false -return self -end -function AIRBOSS:SetRecoveryTanker(recoverytanker) -self.tanker=recoverytanker -return self -end -function AIRBOSS:SetAWACS(awacs) -self.awacs=awacs -return self -end -function AIRBOSS:SetDefaultPlayerSkill(skill) -self.defaultskill=skill or AIRBOSS.Difficulty.NORMAL -local gotit=false -for _,_skill in pairs(AIRBOSS.Difficulty)do -if _skill==self.defaultskill then -gotit=true -end -end -if not gotit then -self.defaultskill=AIRBOSS.Difficulty.NORMAL -self:E(self.lid..string.format("ERROR: Invalid default skill = %s. Resetting to Naval Aviator.",tostring(skill))) -end -return self -end -function AIRBOSS:SetAutoSave(path,filename) -self.autosave=true -self.autosavepath=path -self.autosavefile=filename -return self -end -function AIRBOSS:SetDebugModeON() -self.Debug=true -return self -end -function AIRBOSS:SetPatrolAdInfinitum(switch) -if switch==false then -self.adinfinitum=false -else -self.adinfinitum=true -end -return self -end -function AIRBOSS:SetMagneticDeclination(declination) -self.magvar=declination or UTILS.GetMagneticDeclination() -return self -end -function AIRBOSS:SetDebugModeOFF() -self.Debug=false -return self -end -function AIRBOSS:GetNextRecoveryTime(InSeconds) -if self.recoverywindow then -if InSeconds then -return self.recoverywindow.START,self.recoverywindow.STOP -else -return UTILS.SecondsToClock(self.recoverywindow.START),UTILS.SecondsToClock(self.recoverywindow.STOP) -end -else -if InSeconds then -return-1,-1 -else -return"?","?" -end -end -end -function AIRBOSS:IsRecovering() -return self:is("Recovering") -end -function AIRBOSS:IsIdle() -return self:is("Idle") -end -function AIRBOSS:IsPaused() -return self:is("Paused") -end -function AIRBOSS:_ActivateBeacons() -self:T(self.lid..string.format("Activating Beacons (TACAN=%s, ICLS=%s)",tostring(self.TACANon),tostring(self.ICLSon))) -if self.TACANon then -self:I(self.lid..string.format("Activating TACAN Channel %d%s (%s)",self.TACANchannel,self.TACANmode,self.TACANmorse)) -self.beacon:ActivateTACAN(self.TACANchannel,self.TACANmode,self.TACANmorse,true) -end -if self.ICLSon then -self:I(self.lid..string.format("Activating ICLS Channel %d (%s)",self.ICLSchannel,self.ICLSmorse)) -self.beacon:ActivateICLS(self.ICLSchannel,self.ICLSmorse) -end -self.Tbeacon=timer.getTime() -end -function AIRBOSS:onafterStart(From,Event,To) -self:I(self.lid..string.format("Starting AIRBOSS v%s for carrier unit %s of type %s on map %s",AIRBOSS.version,self.carrier:GetName(),self.carriertype,self.theatre)) -self:_ActivateBeacons() -self.Cposition=self:GetCoordinate() -self.Corientation=self.carrier:GetOrientationX() -self.Corientlast=self.Corientation -self.Tpupdate=timer.getTime() -if#self.recoverytimes==0 and false then -local Topen=timer.getAbsTime()+15*60 -local Tclose=Topen+3*60*60 -self:AddRecoveryWindow(UTILS.SecondsToClock(Topen),UTILS.SecondsToClock(Tclose)) -end -self:_CheckRecoveryTimes() -self.Tqueue=timer.getTime()-60 -self:HandleEvent(EVENTS.Birth) -self:HandleEvent(EVENTS.Land) -self:HandleEvent(EVENTS.EngineShutdown) -self:HandleEvent(EVENTS.Takeoff) -self:HandleEvent(EVENTS.Crash) -self:HandleEvent(EVENTS.Ejection) -self:HandleEvent(EVENTS.PlayerLeaveUnit,self._PlayerLeft) -self:HandleEvent(EVENTS.MissionEnd) -self:HandleEvent(EVENTS.RemoveUnit) -self.StatusTimer=TIMER:New(self._Status,self):Start(2,0.5) -self:__Status(1) -end -function AIRBOSS:onafterStatus(From,Event,To) -local time=timer.getTime() -if time-self.Tqueue>self.dTqueue then -local clock=UTILS.SecondsToClock(timer.getAbsTime()) -local eta=UTILS.SecondsToClock(self:_GetETAatNextWP()) -local hdg=self:GetHeading() -local pos=self:GetCoordinate() -local speed=self.carrier:GetVelocityKNOTS() -local collision=false -local holdtime=0 -if self.holdtimestamp then -holdtime=timer.getTime()-self.holdtimestamp -end -local NextWP=self:_GetNextWaypoint() -local ExpectedSpeed=UTILS.MpsToKnots(NextWP:GetVelocity()) -if speed<0.5 and ExpectedSpeed>0 and not(self.detour or self.turnintowind)then -if not self.holdtimestamp then -self:E(self.lid..string.format("Carrier came to an unexpected standstill. Trying to re-route in 3 min. Speed=%.1f knots, expected=%.1f knots",speed,ExpectedSpeed)) -self.holdtimestamp=timer.getTime() -else -if holdtime>3*60 then -local coord=self:GetCoordinate():Translate(500,hdg+10) -self:CarrierResumeRoute(coord) -self.holdtimestamp=nil -end -end -end -local text=string.format("Time %s - Status %s (case=%d) - Speed=%.1f kts - Heading=%d - WP=%d - ETA=%s - Turning=%s - Collision Warning=%s - Detour=%s - Turn Into Wind=%s - Holdtime=%d sec", -clock,self:GetState(),self.case,speed,hdg,self.currentwp,eta,tostring(self.turning),tostring(collision),tostring(self.detour),tostring(self.turnintowind),holdtime) -self:T(self.lid..text) -text="Players:" -local i=0 -for _name,_player in pairs(self.players)do -i=i+1 -local player=_player -text=text..string.format("\n%d.) %s: Step=%s, Unit=%s, Airframe=%s",i,tostring(player.name),tostring(player.step),tostring(player.unitname),tostring(player.actype)) -end -if i==0 then -text=text.." none" -end -self:I(self.lid..text) -if collision then -if self.turnintowind then -self:CarrierResumeRoute(self.Creturnto) -if self:IsRecovering()and self.recoverywindow and self.recoverywindow.WIND then -self.recoverywindow.WIND=false -end -end -end -self:_CheckRecoveryTimes() -self:_ScanCarrierZone() -self:_CheckQueue() -self:_CheckCarrierTurning() -self:_CheckPatternUpdate() -self.Tqueue=time -end -if time-self.Tbeacon>self.dTbeacon then -self:_ActivateBeacons() -end -self:__Status(-30) -end -function AIRBOSS:_Status() -self:_CheckPlayerStatus() -self:_CheckAIStatus() -end -function AIRBOSS:_CheckAIStatus() -for _,_flight in pairs(self.Qmarshal)do -local flight=_flight -if flight.ai then -local fuel=flight.group:GetFuelMin()*100 -local text=string.format("Group %s fuel=%.1f %%",flight.groupname,fuel) -self:T3(self.lid..text) -if self.lowfuelAI and fuel=recovery.START then -if time0 then -local extmin=5*npattern -recovery.STOP=recovery.STOP+extmin*60 -local text=string.format("We still got flights in the pattern.\nRecovery time prolonged by %d minutes.\nNow get your act together and no more bolters!",extmin) -self:MessageToPattern(text,"AIRBOSS","99",10,false,nil) -else -self:RecoveryStop() -state="closing now" -recovery.OPEN=false -recovery.OVER=true -end -else -state="closed" -end -end -else -state="in the future" -if nextwindow==nil then -nextwindow=recovery -state="next in line" -end -end -text=text..string.format("\n- Start=%s Stop=%s Case=%d Offset=%d Open=%s Closed=%s Status=\"%s\"",Cstart,Cstop,recovery.CASE,recovery.OFFSET,tostring(recovery.OPEN),tostring(recovery.OVER),state) -end -self:T(self.lid..text) -self.recoverywindow=nil -if self:IsIdle()then -if nextwindow then -self:RecoveryCase(nextwindow.CASE,nextwindow.OFFSET) -if nextwindow.WIND and nextwindow.START-time5 -local _,vwind=self:GetWind() -if vwind<0.1 then -uturn=false -end -if not nextwindow.UTURN then -uturn=false -end -self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s",hdg,wind,UTILS.MpsToKnots(vwind),delta,tostring(uturn))) -local t=math.max(nextwindow.STOP-nextwindow.START+self.dTturn,60*60*24) -local v=UTILS.KnotsToMps(nextwindow.SPEED) -local vmax=self.carrier:GetSpeedMax()/3.6 -v=math.min(v,vmax) -self:CarrierTurnIntoWind(t,v,uturn) -end -self.recoverywindow=nextwindow -else -self:RecoveryCase() -end -else -if currwindow then -self.recoverywindow=currwindow -else -self.recoverywindow=nextwindow -end -end -self:T2({"FF",recoverywindow=self.recoverywindow}) -end -function AIRBOSS:_GetFlightLead(flight) -if flight.name~=flight.seclead then -local lead=self.players[flight.seclead] -return lead,false -else -return flight,true -end -end -function AIRBOSS:onbeforeRecoveryCase(From,Event,To,Case,Offset) -Case=Case or self.defaultcase -Offset=Offset or self.defaultoffset -if Case==self.case and Offset==self.holdingoffset then -return false -end -return true -end -function AIRBOSS:onafterRecoveryCase(From,Event,To,Case,Offset) -Case=Case or self.defaultcase -Offset=Offset or self.defaultoffset -local text=string.format("Switching recovery case %d ==> %d",self.case,Case) -if Case>1 then -text=text..string.format(" Holding offset angle %d degrees.",Offset) -end -MESSAGE:New(text,20,self.alias):ToAllIf(self.Debug) -self:T(self.lid..text) -self.case=Case -self.holdingoffset=Offset -for _,_flight in pairs(self.flights)do -local flight=_flight -if not(self:_InQueue(self.Qmarshal,flight.group)or self:_InQueue(self.Qpattern,flight.group))then -if flight.name~=flight.seclead then -local lead=self.players[flight.seclead] -if lead and not(self:_InQueue(self.Qmarshal,lead.group)or self:_InQueue(self.Qpattern,lead.group))then -flight.case=self.case -end -else -flight.case=self.case -end -end -end -end -function AIRBOSS:onafterRecoveryStart(From,Event,To,Case,Offset) -Case=Case or self.defaultcase -Offset=Offset or self.defaultoffset -self:_MarshalCallRecoveryStart(Case) -self:RecoveryCase(Case,Offset) -end -function AIRBOSS:onafterRecoveryStop(From,Event,To) -self:T(self.lid..string.format("Stopping aircraft recovery.")) -self:_MarshalCallRecoveryStopped(self.case) -if self.turnintowind then -local coord=self.Creturnto -if self.recoverywindow and self.recoverywindow.UTURN==false then -coord=nil -end -self:CarrierResumeRoute(coord) -end -if self.recoverywindow and self.recoverywindow.OPEN==true then -self.recoverywindow.OPEN=false -self.recoverywindow.OVER=true -self:DeleteRecoveryWindow(self.recoverywindow) -end -self:_CheckRecoveryTimes() -end -function AIRBOSS:onafterRecoveryPause(From,Event,To,duration) -self:T(self.lid..string.format("Pausing aircraft recovery.")) -if duration then -self:__RecoveryUnpause(duration) -local clock=UTILS.SecondsToClock(timer.getAbsTime()+duration) -self:_MarshalCallRecoveryPausedResumedAt(clock) -else -local text=string.format("aircraft recovery is paused until further notice.") -self:_MarshalCallRecoveryPausedNotice() -end -end -function AIRBOSS:onafterRecoveryUnpause(From,Event,To) -self:T(self.lid..string.format("Unpausing aircraft recovery.")) -self:_MarshalCallResumeRecovery() -end -function AIRBOSS:onafterPassingWaypoint(From,Event,To,n) -self:I(self.lid..string.format("Carrier passed waypoint %d.",n)) -end -function AIRBOSS:onafterIdle(From,Event,To) -self:T(self.lid..string.format("Carrier goes to idle.")) -end -function AIRBOSS:onafterStop(From,Event,To) -self:I(self.lid..string.format("Stopping airboss script.")) -self:UnHandleEvent(EVENTS.Birth) -self:UnHandleEvent(EVENTS.Land) -self:UnHandleEvent(EVENTS.EngineShutdown) -self:UnHandleEvent(EVENTS.Takeoff) -self:UnHandleEvent(EVENTS.Crash) -self:UnHandleEvent(EVENTS.Ejection) -self:UnHandleEvent(EVENTS.PlayerLeaveUnit) -self:UnHandleEvent(EVENTS.MissionEnd) -self.CallScheduler:Clear() -end -function AIRBOSS:_InitStennis() -self.carrierparam.sterndist=-153 -self.carrierparam.deckheight=19.06 -self.carrierparam.totlength=310 -self.carrierparam.totwidthport=40 -self.carrierparam.totwidthstarboard=30 -self.carrierparam.rwyangle=-9.1359 -self.carrierparam.rwylength=225 -self.carrierparam.rwywidth=20 -self.carrierparam.wire1=46 -self.carrierparam.wire2=46+12 -self.carrierparam.wire3=46+24 -self.carrierparam.wire4=46+35 -self.Platform.name="Platform 5k" -self.Platform.Xmin=-UTILS.NMToMeters(22) -self.Platform.Xmax=nil -self.Platform.Zmin=-UTILS.NMToMeters(30) -self.Platform.Zmax=UTILS.NMToMeters(30) -self.Platform.LimitXmin=nil -self.Platform.LimitXmax=nil -self.Platform.LimitZmin=nil -self.Platform.LimitZmax=nil -self.DirtyUp.name="Dirty Up" -self.DirtyUp.Xmin=-UTILS.NMToMeters(21) -self.DirtyUp.Xmax=nil -self.DirtyUp.Zmin=-UTILS.NMToMeters(30) -self.DirtyUp.Zmax=UTILS.NMToMeters(30) -self.DirtyUp.LimitXmin=nil -self.DirtyUp.LimitXmax=nil -self.DirtyUp.LimitZmin=nil -self.DirtyUp.LimitZmax=nil -self.Bullseye.name="Bullseye" -self.Bullseye.Xmin=-UTILS.NMToMeters(11) -self.Bullseye.Xmax=nil -self.Bullseye.Zmin=-UTILS.NMToMeters(30) -self.Bullseye.Zmax=UTILS.NMToMeters(30) -self.Bullseye.LimitXmin=nil -self.Bullseye.LimitXmax=nil -self.Bullseye.LimitZmin=nil -self.Bullseye.LimitZmax=nil -self.BreakEntry.name="Break Entry" -self.BreakEntry.Xmin=-UTILS.NMToMeters(4) -self.BreakEntry.Xmax=nil -self.BreakEntry.Zmin=-UTILS.NMToMeters(0.5) -self.BreakEntry.Zmax=UTILS.NMToMeters(1.5) -self.BreakEntry.LimitXmin=0 -self.BreakEntry.LimitXmax=nil -self.BreakEntry.LimitZmin=nil -self.BreakEntry.LimitZmax=nil -self.BreakEarly.name="Early Break" -self.BreakEarly.Xmin=-UTILS.NMToMeters(1) -self.BreakEarly.Xmax=UTILS.NMToMeters(5) -self.BreakEarly.Zmin=-UTILS.NMToMeters(2) -self.BreakEarly.Zmax=UTILS.NMToMeters(1) -self.BreakEarly.LimitXmin=0 -self.BreakEarly.LimitXmax=nil -self.BreakEarly.LimitZmin=-UTILS.NMToMeters(0.2) -self.BreakEarly.LimitZmax=nil -self.BreakLate.name="Late Break" -self.BreakLate.Xmin=-UTILS.NMToMeters(1) -self.BreakLate.Xmax=UTILS.NMToMeters(5) -self.BreakLate.Zmin=-UTILS.NMToMeters(2) -self.BreakLate.Zmax=UTILS.NMToMeters(1) -self.BreakLate.LimitXmin=0 -self.BreakLate.LimitXmax=nil -self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.8) -self.BreakLate.LimitZmax=nil -self.Abeam.name="Abeam Position" -self.Abeam.Xmin=-UTILS.NMToMeters(5) -self.Abeam.Xmax=UTILS.NMToMeters(5) -self.Abeam.Zmin=-UTILS.NMToMeters(2) -self.Abeam.Zmax=500 -self.Abeam.LimitXmin=-200 -self.Abeam.LimitXmax=nil -self.Abeam.LimitZmin=nil -self.Abeam.LimitZmax=nil -self.Ninety.name="Ninety" -self.Ninety.Xmin=-UTILS.NMToMeters(4) -self.Ninety.Xmax=0 -self.Ninety.Zmin=-UTILS.NMToMeters(2) -self.Ninety.Zmax=nil -self.Ninety.LimitXmin=nil -self.Ninety.LimitXmax=nil -self.Ninety.LimitZmin=nil -self.Ninety.LimitZmax=-UTILS.NMToMeters(0.6) -self.Wake.name="Wake" -self.Wake.Xmin=-UTILS.NMToMeters(4) -self.Wake.Xmax=0 -self.Wake.Zmin=-2000 -self.Wake.Zmax=nil -self.Wake.LimitXmin=nil -self.Wake.LimitXmax=nil -self.Wake.LimitZmin=0 -self.Wake.LimitZmax=nil -self.Final.name="Final" -self.Final.Xmin=-UTILS.NMToMeters(4) -self.Final.Xmax=0 -self.Final.Zmin=-2000 -self.Final.Zmax=nil -self.Final.LimitXmin=nil -self.Final.LimitXmax=nil -self.Final.LimitZmin=nil -self.Final.LimitZmax=nil -self.Groove.name="Groove" -self.Groove.Xmin=-UTILS.NMToMeters(4) -self.Groove.Xmax=nil -self.Groove.Zmin=-UTILS.NMToMeters(2) -self.Groove.Zmax=UTILS.NMToMeters(2) -self.Groove.LimitXmin=nil -self.Groove.LimitXmax=nil -self.Groove.LimitZmin=nil -self.Groove.LimitZmax=nil -end -function AIRBOSS:_InitNimitz() -self:_InitStennis() -self.carrierparam.sterndist=-164 -self.carrierparam.deckheight=20.1494 -self.carrierparam.totlength=332.8 -self.carrierparam.totwidthport=45 -self.carrierparam.totwidthstarboard=35 -self.carrierparam.rwyangle=-9.1359 -self.carrierparam.rwylength=250 -self.carrierparam.rwywidth=25 -self.carrierparam.wire1=55 -self.carrierparam.wire2=67 -self.carrierparam.wire3=79 -self.carrierparam.wire4=92 -end -function AIRBOSS:_InitTarawa() -self:_InitStennis() -self.carrierparam.sterndist=-125 -self.carrierparam.deckheight=21 -self.carrierparam.totlength=245 -self.carrierparam.totwidthport=10 -self.carrierparam.totwidthstarboard=25 -self.carrierparam.rwyangle=0 -self.carrierparam.rwylength=225 -self.carrierparam.rwywidth=15 -self.carrierparam.wire1=nil -self.carrierparam.wire2=nil -self.carrierparam.wire3=nil -self.carrierparam.wire4=nil -self.BreakLate.name="Late Break" -self.BreakLate.Xmin=-UTILS.NMToMeters(1) -self.BreakLate.Xmax=UTILS.NMToMeters(5) -self.BreakLate.Zmin=-UTILS.NMToMeters(1.6) -self.BreakLate.Zmax=UTILS.NMToMeters(1) -self.BreakLate.LimitXmin=0 -self.BreakLate.LimitXmax=nil -self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5) -self.BreakLate.LimitZmax=nil -end -function AIRBOSS:SetVoiceOversMarshalByGabriella(mizfolder) -if mizfolder then -local lastchar=string.sub(mizfolder,-1) -if lastchar~="/"then -mizfolder=mizfolder.."/" -end -self.soundfolderMSH=mizfolder -else -self.soundfolderMSH=self.soundfolder -end -self:I(self.lid..string.format("Marshal Gabriella reporting for duty! Soundfolder=%s",tostring(self.soundfolderMSH))) -self.MarshalCall.AFFIRMATIVE.duration=0.65 -self.MarshalCall.ALTIMETER.duration=0.60 -self.MarshalCall.BRC.duration=0.67 -self.MarshalCall.CARRIERTURNTOHEADING.duration=1.62 -self.MarshalCall.CASE.duration=0.30 -self.MarshalCall.CHARLIETIME.duration=0.77 -self.MarshalCall.CLEAREDFORRECOVERY.duration=0.93 -self.MarshalCall.DECKCLOSED.duration=0.73 -self.MarshalCall.DEGREES.duration=0.48 -self.MarshalCall.EXPECTED.duration=0.50 -self.MarshalCall.FLYNEEDLES.duration=0.89 -self.MarshalCall.HOLDATANGELS.duration=0.81 -self.MarshalCall.HOURS.duration=0.41 -self.MarshalCall.MARSHALRADIAL.duration=0.95 -self.MarshalCall.N0.duration=0.41 -self.MarshalCall.N1.duration=0.30 -self.MarshalCall.N2.duration=0.34 -self.MarshalCall.N3.duration=0.31 -self.MarshalCall.N4.duration=0.34 -self.MarshalCall.N5.duration=0.30 -self.MarshalCall.N6.duration=0.33 -self.MarshalCall.N7.duration=0.38 -self.MarshalCall.N8.duration=0.35 -self.MarshalCall.N9.duration=0.35 -self.MarshalCall.NEGATIVE.duration=0.60 -self.MarshalCall.NEWFB.duration=0.95 -self.MarshalCall.OPS.duration=0.23 -self.MarshalCall.POINT.duration=0.38 -self.MarshalCall.RADIOCHECK.duration=1.27 -self.MarshalCall.RECOVERY.duration=0.60 -self.MarshalCall.RECOVERYOPSSTOPPED.duration=1.25 -self.MarshalCall.RECOVERYPAUSEDNOTICE.duration=2.55 -self.MarshalCall.RECOVERYPAUSEDRESUMED.duration=2.55 -self.MarshalCall.REPORTSEEME.duration=0.87 -self.MarshalCall.RESUMERECOVERY.duration=1.55 -self.MarshalCall.ROGER.duration=0.50 -self.MarshalCall.SAYNEEDLES.duration=0.82 -self.MarshalCall.STACKFULL.duration=5.70 -self.MarshalCall.STARTINGRECOVERY.duration=1.61 -end -function AIRBOSS:SetVoiceOversMarshalByRaynor(mizfolder) -if mizfolder then -local lastchar=string.sub(mizfolder,-1) -if lastchar~="/"then -mizfolder=mizfolder.."/" -end -self.soundfolderMSH=mizfolder -else -self.soundfolderMSH=self.soundfolder -end -self:I(self.lid..string.format("Marshal Raynor reporting for duty! Soundfolder=%s",tostring(self.soundfolderMSH))) -self.MarshalCall.AFFIRMATIVE.duration=0.70 -self.MarshalCall.ALTIMETER.duration=0.60 -self.MarshalCall.BRC.duration=0.60 -self.MarshalCall.CARRIERTURNTOHEADING.duration=1.87 -self.MarshalCall.CASE.duration=0.60 -self.MarshalCall.CHARLIETIME.duration=0.81 -self.MarshalCall.CLEAREDFORRECOVERY.duration=1.21 -self.MarshalCall.DECKCLOSED.duration=0.86 -self.MarshalCall.DEGREES.duration=0.55 -self.MarshalCall.EXPECTED.duration=0.61 -self.MarshalCall.FLYNEEDLES.duration=0.90 -self.MarshalCall.HOLDATANGELS.duration=0.91 -self.MarshalCall.HOURS.duration=0.54 -self.MarshalCall.MARSHALRADIAL.duration=0.80 -self.MarshalCall.N0.duration=0.38 -self.MarshalCall.N1.duration=0.30 -self.MarshalCall.N2.duration=0.30 -self.MarshalCall.N3.duration=0.30 -self.MarshalCall.N4.duration=0.32 -self.MarshalCall.N5.duration=0.41 -self.MarshalCall.N6.duration=0.48 -self.MarshalCall.N7.duration=0.51 -self.MarshalCall.N8.duration=0.38 -self.MarshalCall.N9.duration=0.34 -self.MarshalCall.NEGATIVE.duration=0.60 -self.MarshalCall.NEWFB.duration=1.10 -self.MarshalCall.OPS.duration=0.46 -self.MarshalCall.POINT.duration=0.21 -self.MarshalCall.RADIOCHECK.duration=0.95 -self.MarshalCall.RECOVERY.duration=0.63 -self.MarshalCall.RECOVERYOPSSTOPPED.duration=1.36 -self.MarshalCall.RECOVERYPAUSEDNOTICE.duration=2.8 -self.MarshalCall.RECOVERYPAUSEDRESUMED.duration=2.75 -self.MarshalCall.REPORTSEEME.duration=1.06 -self.MarshalCall.RESUMERECOVERY.duration=1.41 -self.MarshalCall.ROGER.duration=0.41 -self.MarshalCall.SAYNEEDLES.duration=0.79 -self.MarshalCall.STACKFULL.duration=4.70 -self.MarshalCall.STARTINGRECOVERY.duration=2.06 -end -function AIRBOSS:SetVoiceOversLSOByRaynor(mizfolder) -if mizfolder then -local lastchar=string.sub(mizfolder,-1) -if lastchar~="/"then -mizfolder=mizfolder.."/" -end -self.soundfolderLSO=mizfolder -else -self.soundfolderLSO=self.soundfolder -end -self:I(self.lid..string.format("LSO Raynor reporting for duty! Soundfolder=%s",tostring(self.soundfolderLSO))) -self.LSOCall.BOLTER.duration=0.75 -self.LSOCall.CALLTHEBALL.duration=0.625 -self.LSOCall.CHECK.duration=0.40 -self.LSOCall.CLEAREDTOLAND.duration=0.85 -self.LSOCall.COMELEFT.duration=0.60 -self.LSOCall.DEPARTANDREENTER.duration=1.10 -self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.30 -self.LSOCall.EXPECTSPOT75.duration=1.85 -self.LSOCall.FAST.duration=0.75 -self.LSOCall.FOULDECK.duration=0.75 -self.LSOCall.HIGH.duration=0.65 -self.LSOCall.IDLE.duration=0.40 -self.LSOCall.LONGINGROOVE.duration=1.25 -self.LSOCall.LOW.duration=0.60 -self.LSOCall.N0.duration=0.38 -self.LSOCall.N1.duration=0.30 -self.LSOCall.N2.duration=0.30 -self.LSOCall.N3.duration=0.30 -self.LSOCall.N4.duration=0.32 -self.LSOCall.N5.duration=0.41 -self.LSOCall.N6.duration=0.48 -self.LSOCall.N7.duration=0.51 -self.LSOCall.N8.duration=0.38 -self.LSOCall.N9.duration=0.34 -self.LSOCall.PADDLESCONTACT.duration=0.91 -self.LSOCall.POWER.duration=0.45 -self.LSOCall.RADIOCHECK.duration=0.90 -self.LSOCall.RIGHTFORLINEUP.duration=0.70 -self.LSOCall.ROGERBALL.duration=0.72 -self.LSOCall.SLOW.duration=0.63 -self.LSOCall.STABILIZED.duration=0.75 -self.LSOCall.WAVEOFF.duration=0.55 -self.LSOCall.WELCOMEABOARD.duration=0.80 -end -function AIRBOSS:SetVoiceOversLSOByFF(mizfolder) -if mizfolder then -local lastchar=string.sub(mizfolder,-1) -if lastchar~="/"then -mizfolder=mizfolder.."/" -end -self.soundfolderLSO=mizfolder -else -self.soundfolderLSO=self.soundfolder -end -self:I(self.lid..string.format("LSO FF reporting for duty! Soundfolder=%s",tostring(self.soundfolderLSO))) -self.LSOCall.BOLTER.duration=0.75 -self.LSOCall.CALLTHEBALL.duration=0.60 -self.LSOCall.CHECK.duration=0.45 -self.LSOCall.CLEAREDTOLAND.duration=1.00 -self.LSOCall.COMELEFT.duration=0.60 -self.LSOCall.DEPARTANDREENTER.duration=1.10 -self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.20 -self.LSOCall.EXPECTSPOT75.duration=2.00 -self.LSOCall.FAST.duration=0.70 -self.LSOCall.FOULDECK.duration=0.62 -self.LSOCall.HIGH.duration=0.65 -self.LSOCall.IDLE.duration=0.45 -self.LSOCall.LONGINGROOVE.duration=1.20 -self.LSOCall.LOW.duration=0.50 -self.LSOCall.N0.duration=0.40 -self.LSOCall.N1.duration=0.25 -self.LSOCall.N2.duration=0.37 -self.LSOCall.N3.duration=0.37 -self.LSOCall.N4.duration=0.39 -self.LSOCall.N5.duration=0.39 -self.LSOCall.N6.duration=0.40 -self.LSOCall.N7.duration=0.40 -self.LSOCall.N8.duration=0.37 -self.LSOCall.N9.duration=0.40 -self.LSOCall.PADDLESCONTACT.duration=1.00 -self.LSOCall.POWER.duration=0.50 -self.LSOCall.RADIOCHECK.duration=1.10 -self.LSOCall.RIGHTFORLINEUP.duration=0.80 -self.LSOCall.ROGERBALL.duration=1.00 -self.LSOCall.SLOW.duration=0.65 -self.LSOCall.SLOW.duration=0.59 -self.LSOCall.STABILIZED.duration=0.90 -self.LSOCall.WAVEOFF.duration=0.60 -self.LSOCall.WELCOMEABOARD.duration=1.00 -end -function AIRBOSS:SetVoiceOversMarshalByFF(mizfolder) -if mizfolder then -local lastchar=string.sub(mizfolder,-1) -if lastchar~="/"then -mizfolder=mizfolder.."/" -end -self.soundfolderMSH=mizfolder -else -self.soundfolderMSH=self.soundfolder -end -self:I(self.lid..string.format("Marshal FF reporting for duty! Soundfolder=%s",tostring(self.soundfolderMSH))) -self.MarshalCall.AFFIRMATIVE.duration=0.90 -self.MarshalCall.ALTIMETER.duration=0.85 -self.MarshalCall.BRC.duration=0.80 -self.MarshalCall.CARRIERTURNTOHEADING.duration=2.48 -self.MarshalCall.CASE.duration=0.40 -self.MarshalCall.CHARLIETIME.duration=0.90 -self.MarshalCall.CLEAREDFORRECOVERY.duration=1.25 -self.MarshalCall.DECKCLOSED.duration=1.10 -self.MarshalCall.DEGREES.duration=0.60 -self.MarshalCall.EXPECTED.duration=0.55 -self.MarshalCall.FLYNEEDLES.duration=0.90 -self.MarshalCall.HOLDATANGELS.duration=1.10 -self.MarshalCall.HOURS.duration=0.60 -self.MarshalCall.MARSHALRADIAL.duration=1.10 -self.MarshalCall.N0.duration=0.40 -self.MarshalCall.N1.duration=0.25 -self.MarshalCall.N2.duration=0.37 -self.MarshalCall.N3.duration=0.37 -self.MarshalCall.N4.duration=0.39 -self.MarshalCall.N5.duration=0.39 -self.MarshalCall.N6.duration=0.40 -self.MarshalCall.N7.duration=0.40 -self.MarshalCall.N8.duration=0.37 -self.MarshalCall.N9.duration=0.40 -self.MarshalCall.NEGATIVE.duration=0.80 -self.MarshalCall.NEWFB.duration=1.35 -self.MarshalCall.OPS.duration=0.48 -self.MarshalCall.POINT.duration=0.33 -self.MarshalCall.RADIOCHECK.duration=1.20 -self.MarshalCall.RECOVERY.duration=0.70 -self.MarshalCall.RECOVERYOPSSTOPPED.duration=1.65 -self.MarshalCall.RECOVERYPAUSEDNOTICE.duration=2.9 -self.MarshalCall.RECOVERYPAUSEDRESUMED.duration=3.40 -self.MarshalCall.REPORTSEEME.duration=0.95 -self.MarshalCall.RESUMERECOVERY.duration=1.75 -self.MarshalCall.ROGER.duration=0.53 -self.MarshalCall.SAYNEEDLES.duration=0.90 -self.MarshalCall.STACKFULL.duration=6.35 -self.MarshalCall.STARTINGRECOVERY.duration=2.65 -end -function AIRBOSS:_InitVoiceOvers() -self.LSOCall={ -BOLTER={ -file="LSO-BolterBolter", -suffix="ogg", -loud=false, -subtitle="Bolter, Bolter", -duration=0.75, -subduration=5, -}, -CALLTHEBALL={ -file="LSO-CallTheBall", -suffix="ogg", -loud=false, -subtitle="Call the ball", -duration=0.6, -subduration=2, -}, -CHECK={ -file="LSO-Check", -suffix="ogg", -loud=false, -subtitle="Check", -duration=0.45, -subduration=2.5, -}, -CLEAREDTOLAND={ -file="LSO-ClearedToLand", -suffix="ogg", -loud=false, -subtitle="Cleared to land", -duration=1.0, -subduration=5, -}, -COMELEFT={ -file="LSO-ComeLeft", -suffix="ogg", -loud=true, -subtitle="Come left", -duration=0.60, -subduration=1, -}, -RADIOCHECK={ -file="LSO-RadioCheck", -suffix="ogg", -loud=false, -subtitle="Paddles, radio check", -duration=1.1, -subduration=5, -}, -RIGHTFORLINEUP={ -file="LSO-RightForLineup", -suffix="ogg", -loud=true, -subtitle="Right for line up", -duration=0.80, -subduration=1, -}, -HIGH={ -file="LSO-High", -suffix="ogg", -loud=true, -subtitle="You're high", -duration=0.65, -subduration=1, -}, -LOW={ -file="LSO-Low", -suffix="ogg", -loud=true, -subtitle="You're low", -duration=0.50, -subduration=1, -}, -POWER={ -file="LSO-Power", -suffix="ogg", -loud=true, -subtitle="Power", -duration=0.50, -subduration=1, -}, -SLOW={ -file="LSO-Slow", -suffix="ogg", -loud=true, -subtitle="You're slow", -duration=0.65, -subduration=1, -}, -FAST={ -file="LSO-Fast", -suffix="ogg", -loud=true, -subtitle="You're fast", -duration=0.70, -subduration=1, -}, -ROGERBALL={ -file="LSO-RogerBall", -suffix="ogg", -loud=false, -subtitle="Roger ball", -duration=1.00, -subduration=2, -}, -WAVEOFF={ -file="LSO-WaveOff", -suffix="ogg", -loud=false, -subtitle="Wave off", -duration=0.6, -subduration=5, -}, -LONGINGROOVE={ -file="LSO-LongInTheGroove", -suffix="ogg", -loud=false, -subtitle="You're long in the groove", -duration=1.2, -subduration=5, -}, -FOULDECK={ -file="LSO-FoulDeck", -suffix="ogg", -loud=false, -subtitle="Foul deck", -duration=0.62, -subduration=5, -}, -DEPARTANDREENTER={ -file="LSO-DepartAndReenter", -suffix="ogg", -loud=false, -subtitle="Depart and re-enter", -duration=1.1, -subduration=5, -}, -PADDLESCONTACT={ -file="LSO-PaddlesContact", -suffix="ogg", -loud=false, -subtitle="Paddles, contact", -duration=1.0, -subduration=5, -}, -WELCOMEABOARD={ -file="LSO-WelcomeAboard", -suffix="ogg", -loud=false, -subtitle="Welcome aboard", -duration=1.0, -subduration=5, -}, -EXPECTHEAVYWAVEOFF={ -file="LSO-ExpectHeavyWaveoff", -suffix="ogg", -loud=false, -subtitle="Expect heavy waveoff", -duration=1.2, -subduration=5, -}, -EXPECTSPOT75={ -file="LSO-ExpectSpot75", -suffix="ogg", -loud=false, -subtitle="Expect spot 7.5", -duration=2.0, -subduration=5, -}, -STABILIZED={ -file="LSO-Stabilized", -suffix="ogg", -loud=false, -subtitle="Stabilized", -duration=0.9, -subduration=5, -}, -IDLE={ -file="LSO-Idle", -suffix="ogg", -loud=false, -subtitle="Idle", -duration=0.45, -subduration=5, -}, -N0={ -file="LSO-N0", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N1={ -file="LSO-N1", -suffix="ogg", -loud=false, -subtitle="", -duration=0.25, -}, -N2={ -file="LSO-N2", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N3={ -file="LSO-N3", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N4={ -file="LSO-N4", -suffix="ogg", -loud=false, -subtitle="", -duration=0.39, -}, -N5={ -file="LSO-N5", -suffix="ogg", -loud=false, -subtitle="", -duration=0.39, -}, -N6={ -file="LSO-N6", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N7={ -file="LSO-N7", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N8={ -file="LSO-N8", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N9={ -file="LSO-N9", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -CLICK={ -file="AIRBOSS-RadioClick", -suffix="ogg", -loud=false, -subtitle="", -duration=0.35, -}, -NOISE={ -file="AIRBOSS-Noise", -suffix="ogg", -loud=false, -subtitle="", -duration=3.6, -}, -SPINIT={ -file="AIRBOSS-SpinIt", -suffix="ogg", -loud=false, -subtitle="", -duration=0.73, -subduration=5, -}, -} -self.PilotCall={ -N0={ -file="PILOT-N0", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N1={ -file="PILOT-N1", -suffix="ogg", -loud=false, -subtitle="", -duration=0.25, -}, -N2={ -file="PILOT-N2", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N3={ -file="PILOT-N3", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N4={ -file="PILOT-N4", -suffix="ogg", -loud=false, -subtitle="", -duration=0.39, -}, -N5={ -file="PILOT-N5", -suffix="ogg", -loud=false, -subtitle="", -duration=0.39, -}, -N6={ -file="PILOT-N6", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N7={ -file="PILOT-N7", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N8={ -file="PILOT-N8", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N9={ -file="PILOT-N9", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -POINT={ -file="PILOT-Point", -suffix="ogg", -loud=false, -subtitle="", -duration=0.33, -}, -SKYHAWK={ -file="PILOT-Skyhawk", -suffix="ogg", -loud=false, -subtitle="", -duration=0.95, -subduration=5, -}, -HARRIER={ -file="PILOT-Harrier", -suffix="ogg", -loud=false, -subtitle="", -duration=0.58, -subduration=5, -}, -HAWKEYE={ -file="PILOT-Hawkeye", -suffix="ogg", -loud=false, -subtitle="", -duration=0.63, -subduration=5, -}, -TOMCAT={ -file="PILOT-Tomcat", -suffix="ogg", -loud=false, -subtitle="", -duration=0.66, -subduration=5, -}, -HORNET={ -file="PILOT-Hornet", -suffix="ogg", -loud=false, -subtitle="", -duration=0.56, -subduration=5, -}, -VIKING={ -file="PILOT-Viking", -suffix="ogg", -loud=false, -subtitle="", -duration=0.61, -subduration=5, -}, -BALL={ -file="PILOT-Ball", -suffix="ogg", -loud=false, -subtitle="", -duration=0.50, -subduration=5, -}, -BINGOFUEL={ -file="PILOT-BingoFuel", -suffix="ogg", -loud=false, -subtitle="", -duration=0.80, -}, -GASATDIVERT={ -file="PILOT-GasAtDivert", -suffix="ogg", -loud=false, -subtitle="", -duration=1.80, -}, -GASATTANKER={ -file="PILOT-GasAtTanker", -suffix="ogg", -loud=false, -subtitle="", -duration=1.95, -}, -} -self.MarshalCall={ -AFFIRMATIVE={ -file="MARSHAL-Affirmative", -suffix="ogg", -loud=false, -subtitle="", -duration=0.90, -}, -ALTIMETER={ -file="MARSHAL-Altimeter", -suffix="ogg", -loud=false, -subtitle="", -duration=0.85, -}, -BRC={ -file="MARSHAL-BRC", -suffix="ogg", -loud=false, -subtitle="", -duration=0.80, -}, -CARRIERTURNTOHEADING={ -file="MARSHAL-CarrierTurnToHeading", -suffix="ogg", -loud=false, -subtitle="", -duration=2.48, -subduration=5, -}, -CASE={ -file="MARSHAL-Case", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -CHARLIETIME={ -file="MARSHAL-CharlieTime", -suffix="ogg", -loud=false, -subtitle="", -duration=0.90, -}, -CLEAREDFORRECOVERY={ -file="MARSHAL-ClearedForRecovery", -suffix="ogg", -loud=false, -subtitle="", -duration=1.25, -}, -DECKCLOSED={ -file="MARSHAL-DeckClosed", -suffix="ogg", -loud=false, -subtitle="", -duration=1.10, -subduration=5, -}, -DEGREES={ -file="MARSHAL-Degrees", -suffix="ogg", -loud=false, -subtitle="", -duration=0.60, -}, -EXPECTED={ -file="MARSHAL-Expected", -suffix="ogg", -loud=false, -subtitle="", -duration=0.55, -}, -FLYNEEDLES={ -file="MARSHAL-FlyYourNeedles", -suffix="ogg", -loud=false, -subtitle="Fly your needles", -duration=0.9, -subduration=5, -}, -HOLDATANGELS={ -file="MARSHAL-HoldAtAngels", -suffix="ogg", -loud=false, -subtitle="", -duration=1.10, -}, -HOURS={ -file="MARSHAL-Hours", -suffix="ogg", -loud=false, -subtitle="", -duration=0.60, -subduration=5, -}, -MARSHALRADIAL={ -file="MARSHAL-MarshalRadial", -suffix="ogg", -loud=false, -subtitle="", -duration=1.10, -}, -N0={ -file="MARSHAL-N0", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N1={ -file="MARSHAL-N1", -suffix="ogg", -loud=false, -subtitle="", -duration=0.25, -}, -N2={ -file="MARSHAL-N2", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N3={ -file="MARSHAL-N3", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N4={ -file="MARSHAL-N4", -suffix="ogg", -loud=false, -subtitle="", -duration=0.39, -}, -N5={ -file="MARSHAL-N5", -suffix="ogg", -loud=false, -subtitle="", -duration=0.39, -}, -N6={ -file="MARSHAL-N6", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N7={ -file="MARSHAL-N7", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -N8={ -file="MARSHAL-N8", -suffix="ogg", -loud=false, -subtitle="", -duration=0.37, -}, -N9={ -file="MARSHAL-N9", -suffix="ogg", -loud=false, -subtitle="", -duration=0.40, -}, -NEGATIVE={ -file="MARSHAL-Negative", -suffix="ogg", -loud=false, -subtitle="", -duration=0.80, -subduration=5, -}, -NEWFB={ -file="MARSHAL-NewFB", -suffix="ogg", -loud=false, -subtitle="", -duration=1.35, -}, -OPS={ -file="MARSHAL-Ops", -suffix="ogg", -loud=false, -subtitle="", -duration=0.48, -}, -POINT={ -file="MARSHAL-Point", -suffix="ogg", -loud=false, -subtitle="", -duration=0.33, -}, -RADIOCHECK={ -file="MARSHAL-RadioCheck", -suffix="ogg", -loud=false, -subtitle="Radio check", -duration=1.20, -subduration=5, -}, -RECOVERY={ -file="MARSHAL-Recovery", -suffix="ogg", -loud=false, -subtitle="", -duration=0.70, -subduration=5, -}, -RECOVERYOPSSTOPPED={ -file="MARSHAL-RecoveryOpsStopped", -suffix="ogg", -loud=false, -subtitle="", -duration=1.65, -subduration=5, -}, -RECOVERYPAUSEDNOTICE={ -file="MARSHAL-RecoveryPausedNotice", -suffix="ogg", -loud=false, -subtitle="aircraft recovery paused until further notice", -duration=2.90, -subduration=5, -}, -RECOVERYPAUSEDRESUMED={ -file="MARSHAL-RecoveryPausedResumed", -suffix="ogg", -loud=false, -subtitle="", -duration=3.40, -subduration=5, -}, -REPORTSEEME={ -file="MARSHAL-ReportSeeMe", -suffix="ogg", -loud=false, -subtitle="", -duration=0.95, -}, -RESUMERECOVERY={ -file="MARSHAL-ResumeRecovery", -suffix="ogg", -loud=false, -subtitle="resuming aircraft recovery", -duration=1.75, -subduraction=5, -}, -ROGER={ -file="MARSHAL-Roger", -suffix="ogg", -loud=false, -subtitle="", -duration=0.53, -subduration=5, -}, -SAYNEEDLES={ -file="MARSHAL-SayNeedles", -suffix="ogg", -loud=false, -subtitle="Say needles", -duration=0.90, -subduration=5, -}, -STACKFULL={ -file="MARSHAL-StackFull", -suffix="ogg", -loud=false, -subtitle="Marshal Stack is currently full. Hold outside 10 NM zone and wait for further instructions", -duration=6.35, -subduration=10, -}, -STARTINGRECOVERY={ -file="MARSHAL-StartingRecovery", -suffix="ogg", -loud=false, -subtitle="", -duration=2.65, -subduration=5, -}, -CLICK={ -file="AIRBOSS-RadioClick", -suffix="ogg", -loud=false, -subtitle="", -duration=0.35, -}, -NOISE={ -file="AIRBOSS-Noise", -suffix="ogg", -loud=false, -subtitle="", -duration=3.6, -}, -} -self:SetVoiceOversLSOByRaynor() -self:SetVoiceOversMarshalByRaynor() -end -function AIRBOSS:SetVoiceOver(radiocall,duration,subtitle,subduration,filename,suffix) -radiocall.duration=duration -radiocall.subtitle=subtitle or radiocall.subtitle -radiocall.file=filename -radiocall.suffix=suffix or".ogg" -end -function AIRBOSS:_GetAircraftAoA(playerData) -local hornet=playerData.actype==AIRBOSS.AircraftCarrier.HORNET -local goshawk=playerData.actype==AIRBOSS.AircraftCarrier.T45C -local skyhawk=playerData.actype==AIRBOSS.AircraftCarrier.A4EC -local harrier=playerData.actype==AIRBOSS.AircraftCarrier.AV8B -local tomcat=playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B -local aoa={} -if hornet then -aoa.SLOW=9.8 -aoa.Slow=9.3 -aoa.OnSpeedMax=8.8 -aoa.OnSpeed=8.1 -aoa.OnSpeedMin=7.4 -aoa.Fast=6.9 -aoa.FAST=6.3 -elseif tomcat then -aoa.SLOW=self:_AoAUnit2Deg(playerData,17.0) -aoa.Slow=self:_AoAUnit2Deg(playerData,16.0) -aoa.OnSpeedMax=self:_AoAUnit2Deg(playerData,15.5) -aoa.OnSpeed=self:_AoAUnit2Deg(playerData,15.0) -aoa.OnSpeedMin=self:_AoAUnit2Deg(playerData,14.5) -aoa.Fast=self:_AoAUnit2Deg(playerData,14.0) -aoa.FAST=self:_AoAUnit2Deg(playerData,13.0) -elseif goshawk then -aoa.SLOW=8.00 -aoa.Slow=7.75 -aoa.OnSpeedMax=7.25 -aoa.OnSpeed=7.00 -aoa.OnSpeedMin=6.75 -aoa.Fast=6.25 -aoa.FAST=6.00 -elseif skyhawk then -aoa.SLOW=9.50 -aoa.Slow=9.25 -aoa.OnSpeedMax=9.00 -aoa.OnSpeed=8.75 -aoa.OnSpeedMin=8.50 -aoa.Fast=8.25 -aoa.FAST=8.00 -elseif harrier then -aoa.SLOW=14.0 -aoa.Slow=13.0 -aoa.OnSpeedMax=12.0 -aoa.OnSpeed=11.0 -aoa.OnSpeedMin=10.0 -aoa.Fast=9.0 -aoa.FAST=8.0 -end -return aoa -end -function AIRBOSS:_AoAUnit2Deg(playerData,aoaunits) -local degrees=aoaunits -if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then -degrees=-10+50/30*aoaunits -degrees=0.918*aoaunits-3.411 -elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then -degrees=0.5*aoaunits -end -return degrees -end -function AIRBOSS:_AoADeg2Units(playerData,degrees) -local aoaunits=degrees -if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then -aoaunits=(degrees+10)*30/50 -aoaunits=1.089*degrees+3.715 -elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then -aoaunits=2*degrees -end -return aoaunits -end -function AIRBOSS:_GetAircraftParameters(playerData,step) -step=step or playerData.step -local hornet=playerData.actype==AIRBOSS.AircraftCarrier.HORNET -local skyhawk=playerData.actype==AIRBOSS.AircraftCarrier.A4EC -local tomcat=playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B -local harrier=playerData.actype==AIRBOSS.AircraftCarrier.AV8B -local alt -local aoa -local dist -local speed -local aoaac=self:_GetAircraftAoA(playerData) -if step==AIRBOSS.PatternStep.PLATFORM then -alt=UTILS.FeetToMeters(5000) -speed=UTILS.KnotsToMps(250) -elseif step==AIRBOSS.PatternStep.ARCIN then -if tomcat then -speed=UTILS.KnotsToMps(150) -else -speed=UTILS.KnotsToMps(250) -end -elseif step==AIRBOSS.PatternStep.ARCOUT then -if tomcat then -speed=UTILS.KnotsToMps(150) -else -speed=UTILS.KnotsToMps(250) -end -elseif step==AIRBOSS.PatternStep.DIRTYUP then -alt=UTILS.FeetToMeters(1200) -elseif step==AIRBOSS.PatternStep.BULLSEYE then -alt=UTILS.FeetToMeters(1200) -dist=-UTILS.NMToMeters(3) -aoa=aoaac.OnSpeed -elseif step==AIRBOSS.PatternStep.INITIAL then -if hornet or tomcat or harrier then -alt=UTILS.FeetToMeters(800) -speed=UTILS.KnotsToMps(350) -elseif skyhawk then -alt=UTILS.FeetToMeters(600) -speed=UTILS.KnotsToMps(250) -elseif goshawk then -alt=UTILS.FeetToMeters(800) -speed=UTILS.KnotsToMps(300) -end -elseif step==AIRBOSS.PatternStep.BREAKENTRY then -if hornet or tomcat or harrier then -alt=UTILS.FeetToMeters(800) -speed=UTILS.KnotsToMps(350) -elseif skyhawk then -alt=UTILS.FeetToMeters(600) -speed=UTILS.KnotsToMps(250) -elseif goshawk then -alt=UTILS.FeetToMeters(800) -speed=UTILS.KnotsToMps(300) -end -elseif step==AIRBOSS.PatternStep.EARLYBREAK then -if hornet or tomcat or harrier or goshawk then -alt=UTILS.FeetToMeters(800) -elseif skyhawk then -alt=UTILS.FeetToMeters(600) -end -elseif step==AIRBOSS.PatternStep.LATEBREAK then -if hornet or tomcat or harrier or goshawk then -alt=UTILS.FeetToMeters(800) -elseif skyhawk then -alt=UTILS.FeetToMeters(600) -end -elseif step==AIRBOSS.PatternStep.ABEAM then -if hornet or tomcat or harrier or goshawk then -alt=UTILS.FeetToMeters(600) -elseif skyhawk then -alt=UTILS.FeetToMeters(500) -end -aoa=aoaac.OnSpeed -if harrier then -dist=UTILS.NMToMeters(0.9) -else -dist=UTILS.NMToMeters(1.2) -end -if goshawk then -dist=UTILS.NMToMeters(0.9) -else -dist=UTILS.NMToMeters(1.1) -end -elseif step==AIRBOSS.PatternStep.NINETY then -if hornet or tomcat then -alt=UTILS.FeetToMeters(500) -elseif goshawk then -alt=UTILS.FeetToMeters(450) -elseif skyhawk then -alt=UTILS.FeetToMeters(500) -elseif harrier then -alt=UTILS.FeetToMeters(425) -end -aoa=aoaac.OnSpeed -elseif step==AIRBOSS.PatternStep.WAKE then -if hornet or goshawk then -alt=UTILS.FeetToMeters(370) -elseif tomcat then -alt=UTILS.FeetToMeters(430) -elseif skyhawk then -alt=UTILS.FeetToMeters(370) -end -aoa=aoaac.OnSpeed -elseif step==AIRBOSS.PatternStep.FINAL then -if hornet or goshawk then -alt=UTILS.FeetToMeters(300) -elseif tomcat then -alt=UTILS.FeetToMeters(360) -elseif skyhawk then -alt=UTILS.FeetToMeters(300) -elseif harrier then -alt=UTILS.FeetToMeters(300) -end -aoa=aoaac.OnSpeed -end -return alt,aoa,dist,speed -end -function AIRBOSS:_GetNextMarshalFight() -for _,_flight in pairs(self.Qmarshal)do -local flight=_flight -local stack=flight.flag -local Tmarshal=timer.getAbsTime()-flight.time -local TmarshalMin=2*60 -if flight.ai then -TmarshalMin=3*60 -end -if flight.holding~=nil and Tmarshal>=TmarshalMin then -if flight.case==1 and stack==1 or flight.case>1 then -if flight.ai then -return flight -else -if flight.step~=AIRBOSS.PatternStep.COMMENCING then -return flight -end -end -end -end -end -return nil -end -function AIRBOSS:_CheckQueue() -if self.Debug then -self:_PrintQueue(self.flights,"All Flights") -end -self:_PrintQueue(self.Qmarshal,"Marshal") -self:_PrintQueue(self.Qpattern,"Pattern") -self:_PrintQueue(self.Qwaiting,"Waiting") -self:_PrintQueue(self.Qspinning,"Spinning") -if self.case>1 then -for _,_flight in pairs(self.Qwaiting)do -local flight=_flight -local removed=self:_RemoveFlightFromQueue(self.Qwaiting,flight) -if removed then -local stack=self:_GetFreeStack(flight.ai) -self:T(self.lid..string.format("Moving flight %s onboard %s from Waiting queue to Case %d Marshal stack %d",flight.groupname,flight.onboard,self.case,stack)) -if flight.ai then -self:_MarshalAI(flight,stack) -else -self:_MarshalPlayer(flight,stack) -end -break -end -end -end -if not self:IsRecovering()then -for _,_flight in pairs(self.Qmarshal)do -local flight=_flight -if(flight.case==1 and self.case>1)or(flight.case>1 and self.case==1)then -local removed=self:_RemoveFlightFromQueue(self.Qmarshal,flight) -if removed then -local stack=self:_GetFreeStack(flight.ai) -self:T(self.lid..string.format("Moving flight %s onboard %s from Marshal Case %d ==> %d Marshal stack %d",flight.groupname,flight.onboard,flight.case,self.case,stack)) -if flight.ai then -self:_MarshalAI(flight,stack) -else -self:_MarshalPlayer(flight,stack) -end -break -elseif flight.case~=self.case then -flight.case=self.case -end -end -end -return -end -local _,npattern=self:_GetQueueInfo(self.Qpattern) -local _,nspinning=self:_GetQueueInfo(self.Qspinning) -local marshalflight=self:_GetNextMarshalFight() -if marshalflight and npattern0 then -local patternflight=self.Qpattern[#self.Qpattern] -pcase=patternflight.case -local npunits=self:_GetFlightUnits(patternflight,false) -Tpattern=timer.getAbsTime()-patternflight.time -self:T(self.lid..string.format("Pattern time of last group %s = %d seconds. # of units=%d.",patternflight.groupname,Tpattern,npunits)) -end -local TpatternMin -if pcase==1 then -TpatternMin=2*60*npunits -else -TpatternMin=2*60*npunits -end -if Tpattern>TpatternMin then -self:T(self.lid..string.format("Sending marshal flight %s to pattern.",marshalflight.groupname)) -self:_ClearForLanding(marshalflight) -end -end -end -function AIRBOSS:_ClearForLanding(flight) -if flight.ai then -self:_RemoveFlightFromMarshalQueue(flight,false) -self:_LandAI(flight) -self:_MarshalCallClearedForRecovery(flight.onboard,flight.case) -else -if flight.step~=AIRBOSS.PatternStep.COMMENCING then -self:_MarshalCallClearedForRecovery(flight.onboard,flight.case) -flight.time=timer.getAbsTime() -end -self:_SetPlayerStep(flight,AIRBOSS.PatternStep.COMMENCING,3) -end -end -function AIRBOSS:_SetPlayerStep(playerData,step,delay) -if delay and delay>0 then -self:ScheduleOnce(delay,self._SetPlayerStep,self,playerData,step) -else -if playerData then -playerData.step=step -playerData.warning=nil -self:_StepHint(playerData) -end -end -end -function AIRBOSS:_ScanCarrierZone() -local coord=self:GetCoordinate() -local RCCZ=self.zoneCCA:GetRadius() -self:T(self.lid..string.format("Scanning Carrier Controlled Area. Radius=%.1f NM.",UTILS.MetersToNM(RCCZ))) -local _,_,_,unitscan=coord:ScanObjects(RCCZ,true,false,false) -local insideCCA={} -for _,_unit in pairs(unitscan)do -local unit=_unit -local airborne=unit:IsAir() -local inzone=unit:IsInZone(self.zoneCCA) -local friendly=self:GetCoalition()==unit:GetCoalition() -local carrierac=self:_IsCarrierAircraft(unit) -if airborne and inzone and friendly and carrierac then -local group=unit:GetGroup() -local groupname=group:GetName() -if insideCCA[groupname]==nil then -insideCCA[groupname]=group -end -end -end -for groupname,_group in pairs(insideCCA)do -local group=_group -local knownflight=self:_GetFlightFromGroupInQueue(group,self.flights) -local actype=group:GetTypeName() -if knownflight then -if knownflight.ai and knownflight.flag==-100 and self.handleai then -local putintomarshal=false -local flight=_DATABASE:GetFlightGroup(groupname) -if flight and flight:IsInbound()and flight.destbase:GetName()==self.carrier:GetName()then -if flight.ishelo then -else -putintomarshal=true -end -flight.airboss=self -end -if putintomarshal then -local stack=self:_GetFreeStack(knownflight.ai) -local respawn=self.respawnAI -if stack then -self:_MarshalAI(knownflight,stack,respawn) -else -if not self:_InQueue(self.Qwaiting,knownflight.group)then -self:_WaitAI(knownflight,respawn) -end -end -break -end -end -else -if not self:_IsHuman(group)then -self:_CreateFlightGroup(group) -end -end -end -local remove={} -for _,_flight in pairs(self.flights)do -local flight=_flight -if insideCCA[flight.groupname]==nil then -if flight.ai and not(self:_InQueue(self.Qmarshal,flight.group)or self:_InQueue(self.Qpattern,flight.group))then -table.insert(remove,flight) -end -end -end -for _,flight in pairs(remove)do -self:_RemoveFlightFromQueue(self.flights,flight) -end -end -function AIRBOSS:_WaitPlayer(playerData) -if playerData then -local nwaiting=#self.Qwaiting -self:_MarshalCallStackFull(playerData.onboard,nwaiting) -table.insert(self.Qwaiting,playerData) -playerData.time=timer.getAbsTime() -playerData.step=AIRBOSS.PatternStep.WAITING -playerData.warning=nil -for _,_flight in pairs(playerData.section)do -local flight=_flight -flight.step=AIRBOSS.PatternStep.WAITING -flight.time=timer.getAbsTime() -flight.warning=nil -end -end -end -function AIRBOSS:_MarshalPlayer(playerData,stack) -if playerData then -self:_AddMarshalGroup(playerData,stack) -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.HOLDING) -playerData.holding=nil -for _,_flight in pairs(playerData.section)do -local flight=_flight -self:_SetPlayerStep(flight,AIRBOSS.PatternStep.HOLDING) -flight.holding=nil -flight.case=playerData.case -flight.flag=stack -self:Marshal(flight) -end -else -self:E(self.lid.."ERROR: Could not add player to Marshal stack! playerData=nil") -end -end -function AIRBOSS:_WaitAI(flight,respawn) -flight.flag=-99 -table.insert(self.Qwaiting,flight) -local group=flight.group -local groupname=flight.groupname -local speedOrbitMps=UTILS.KnotsToMps(274) -local speedOrbitKmh=UTILS.KnotsToKmph(274) -local speedTransit=UTILS.KnotsToKmph(370) -local cv=self:GetCoordinate() -local fc=group:GetCoordinate() -local hdg=self:GetHeading(false) -local hdgto=cv:HeadingTo(fc) -local angels=math.random(6,10) -local altitude=UTILS.FeetToMeters(angels*1000) -local p0=cv:Translate(UTILS.NMToMeters(11),hdgto):Translate(UTILS.NMToMeters(5),hdg):SetAltitude(altitude) -local wp={} -wp[1]=group:GetCoordinate():WaypointAirTurningPoint(nil,speedTransit,{},"Current Position") -local taskorbit=group:TaskOrbit(p0,altitude,speedOrbitMps) -wp[#wp+1]=p0:WaypointAirTurningPoint(nil,speedOrbitKmh,{taskorbit},string.format("Waiting Orbit at Angels %d",angels)) -if self.Debug then -p0:MarkToAll(string.format("Waiting Orbit of flight %s at Angels %s",groupname,angels)) -end -if respawn then -local Template=group:GetTemplate() -Template.route.points=wp -group=group:Respawn(Template,true) -end -group:WayPointInitialize(wp) -group:Route(wp,1) -end -function AIRBOSS:_MarshalAI(flight,nstack,respawn) -self:F2({flight=flight,nstack=nstack,respawn=respawn}) -if flight==nil or flight.group==nil then -self:E(self.lid.."ERROR: flight or flight.group is nil.") -return -end -if flight.group:GetCoordinate()==nil then -self:E(self.lid.."ERROR: cannot get coordinate of flight group.") -return -end -if not self:_InQueue(self.Qmarshal,flight.group)then -self:_AddMarshalGroup(flight,nstack) -end -local case=flight.case -local ostack=flight.flag -local group=flight.group -local groupname=flight.groupname -flight.flag=nstack -local Carrier=self:GetCoordinate() -local hdg=self:GetHeading() -local speedOrbitMps=UTILS.KnotsToMps(274) -local speedOrbitKmh=UTILS.KnotsToKmph(274) -local speedTransit=UTILS.KnotsToKmph(370) -local altitude -local p0 -local p1 -local p2 -altitude,p1,p2=self:_GetMarshalAltitude(nstack,case) -local wp={} -if not flight.holding then -self:T(self.lid..string.format("Guiding AI flight %s to marshal stack %d-->%d.",groupname,ostack,nstack)) -wp[1]=group:GetCoordinate():WaypointAirTurningPoint(nil,speedTransit,{},"Current Position") -local TaskArrivedHolding=flight.group:TaskFunction("AIRBOSS._ReachedHoldingZone",self,flight) -if case==1 then -local pE=Carrier:Translate(UTILS.NMToMeters(7),hdg-30):SetAltitude(altitude) -p0=Carrier:Translate(UTILS.NMToMeters(5),hdg-135):SetAltitude(altitude) -wp[#wp+1]=pE:WaypointAirTurningPoint(nil,speedTransit,{TaskArrivedHolding},"Entering Case I Marshal Pattern") -else -local radial=self:GetRadial(case,false,true) -p0=p2:Translate(UTILS.NMToMeters(5),radial+90):Translate(UTILS.NMToMeters(5),radial,true) -wp[#wp+1]=p0:WaypointAirTurningPoint(nil,speedTransit,{TaskArrivedHolding},"Entering Case II/III Marshal Pattern") -end -else -self:T(self.lid..string.format("Updating AI flight %s at marshal stack %d-->%d.",groupname,ostack,nstack)) -wp[1]=group:GetCoordinate():WaypointAirTurningPoint(nil,speedOrbitKmh,{},"Current Position") -p0=group:GetCoordinate():Translate(UTILS.NMToMeters(0.2),group:GetHeading(),true) -end -local taskorbit=group:TaskOrbit(p1,altitude,speedOrbitMps,p2) -wp[#wp+1]=p0:WaypointAirTurningPoint(nil,speedOrbitKmh,{taskorbit},string.format("Marshal Orbit Stack %d",nstack)) -if self.Debug then -p0:MarkToAll("WP P0 "..groupname) -p1:MarkToAll("RT P1 "..groupname) -p2:MarkToAll("RT P2 "..groupname) -end -if respawn then -local Template=group:GetTemplate() -Template.route.points=wp -flight.group=group:Respawn(Template,true) -end -flight.group:WayPointInitialize(wp) -flight.group:Route(wp,1) -self:Marshal(flight) -end -function AIRBOSS:_RefuelAI(flight) -local wp={} -local CurrentSpeed=flight.group:GetVelocityKMH() -wp[#wp+1]=flight.group:GetCoordinate():WaypointAirTurningPoint(nil,CurrentSpeed,{},"Current position") -local refuelac=false -local actype=flight.group:GetTypeName() -if actype==AIRBOSS.AircraftCarrier.AV8B or -actype==AIRBOSS.AircraftCarrier.F14A or -actype==AIRBOSS.AircraftCarrier.F14B or -actype==AIRBOSS.AircraftCarrier.F14A_AI or -actype==AIRBOSS.AircraftCarrier.HORNET or -actype==AIRBOSS.AircraftCarrier.FA18C or -actype==AIRBOSS.AircraftCarrier.S3B or -actype==AIRBOSS.AircraftCarrier.S3BTANKER then -refuelac=true -end -local text="" -if self.tanker and refuelac then -local tankerpos=self.tanker.tanker:GetCoordinate() -local TaskRefuel=flight.group:TaskRefueling() -local TaskMarshal=flight.group:TaskFunction("AIRBOSS._TaskFunctionMarshalAI",self,flight) -wp[#wp+1]=tankerpos:WaypointAirTurningPoint(nil,CurrentSpeed,{TaskRefuel,TaskMarshal},"Refueling") -self:_MarshalCallGasAtTanker(flight.onboard) -else -local divertfield=self:GetCoordinate():GetClosestAirbase(Airbase.Category.AIRDROME,self:GetCoalition()) -if divertfield==nil then -divertfield=self:GetCoordinate():GetClosestAirbase(Airbase.Category.AIRDROME,0) -end -if divertfield then -local divertcoord=divertfield:GetCoordinate() -wp[#wp+1]=divertcoord:WaypointAirLanding(UTILS.KnotsToKmph(300),divertfield,{},"Divert Field") -self:_MarshalCallGasAtDivert(flight.onboard,divertfield:GetName()) -local Template=flight.group:GetTemplate() -Template.route.points=wp -flight.group=flight.group:Respawn(Template,true) -else -self:E(self.lid..string.format("WARNING: No recovery tanker or divert field available for group %s.",flight.groupname)) -flight.refueling=true -return -end -end -flight.group:WayPointInitialize(wp) -flight.group:Route(wp,1) -flight.refueling=true -end -function AIRBOSS:_LandAI(flight) -self:T(self.lid..string.format("Landing AI flight %s.",flight.groupname)) -local Speed=UTILS.KnotsToKmph(200) -if flight.actype==AIRBOSS.AircraftCarrier.HORNET or flight.actype==AIRBOSS.AircraftCarrier.FA18C then -Speed=UTILS.KnotsToKmph(200) -elseif flight.actype==AIRBOSS.AircraftCarrier.E2D then -Speed=UTILS.KnotsToKmph(150) -elseif flight.actype==AIRBOSS.AircraftCarrier.F14A_AI or flight.actype==AIRBOSS.AircraftCarrier.F14A or flight.actype==AIRBOSS.AircraftCarrier.F14B then -Speed=UTILS.KnotsToKmph(175) -elseif flight.actype==AIRBOSS.AircraftCarrier.S3B or flight.actype==AIRBOSS.AircraftCarrier.S3BTANKER then -Speed=UTILS.KnotsToKmph(140) -end -local Carrier=self:GetCoordinate() -local hdg=self:GetHeading() -local wp={} -local CurrentSpeed=flight.group:GetVelocityKMH() -wp[#wp+1]=flight.group:GetCoordinate():WaypointAirTurningPoint(nil,CurrentSpeed,{},"Current position") -local alt=UTILS.FeetToMeters(800) -wp[#wp+1]=Carrier:Translate(UTILS.NMToMeters(4),hdg-160):SetAltitude(alt):WaypointAirLanding(Speed,self.airbase,nil,"Landing") -flight.group:WayPointInitialize(wp) -flight.group:Route(wp,0) -end -function AIRBOSS:_GetMarshalAltitude(stack,case) -if stack<=0 then -return 0,nil,nil -end -case=case or self.case -local Carrier=self:GetCoordinate() -local angels0 -local Dist -local p1=nil -local p2=nil -local nstack=stack-1 -if case==1 then -angels0=2 -local hdg=self.carrier:GetHeading() -p1=Carrier -p2=Carrier:Translate(UTILS.NMToMeters(1.5),hdg) -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -p1=Carrier:Translate(UTILS.NMToMeters(1.0),hdg+90) -p2=p1:Translate(2.5,hdg) -end -else -angels0=6 -Dist=UTILS.NMToMeters(nstack+angels0+15) -local radial=self:GetRadial(case,false,true) -local l=UTILS.NMToMeters(10) -p1=Carrier:Translate(Dist+l,radial) -p2=Carrier:Translate(Dist,radial) -end -local altitude=UTILS.FeetToMeters((nstack+angels0)*1000) -p1:SetAltitude(altitude,true) -p2:SetAltitude(altitude,true) -return altitude,p1,p2 -end -function AIRBOSS:_GetCharlieTime(flightgroup) -local stack=flightgroup.flag -if stack<=0 then -return nil -end -local Tnow=timer.getAbsTime() -local Tcharlie=0 -local Trecovery=0 -if self.recoverywindow then -Trecovery=math.max(self.recoverywindow.START-Tnow,0) -else -Trecovery=7*60 -end -for _,_flight in pairs(self.Qmarshal)do -local flight=_flight -local mstack=flight.flag -local Tarrive=0 -local Tholding=3*60 -if stack>0 and mstack>0 and mstack<=stack then -if flight.holding==nil then -local holdingzone=self:_GetZoneHolding(flight.case,1):GetCoordinate() -local d0=holdingzone:Get2DDistance(flight.group:GetCoordinate()) -local v0=flight.group:GetVelocityMPS() -Tarrive=d0/v0 -self:T3(self.lid..string.format("Tarrive=%.1f seconds, Clock %s",Tarrive,UTILS.SecondsToClock(Tnow+Tarrive))) -else -if mstack==1 then -local tholding=timer.getAbsTime()-flight.time -Tholding=math.max(3*60-tholding,0) -end -end -local Tmin=math.max(Tarrive,Trecovery) -Tcharlie=math.max(Tmin,Tcharlie)+Tholding -end -end -Tcharlie=Tcharlie+Tnow -local text=string.format("Charlie time for flight %s (%s) %s",flightgroup.onboard,flightgroup.groupname,UTILS.SecondsToClock(Tcharlie)) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:T(self.lid..text) -return Tcharlie -end -function AIRBOSS:_AddMarshalGroup(flight,stack) -flight.flag=stack -flight.case=self.case -table.insert(self.Qmarshal,flight) -local P=UTILS.hPa2inHg(self:GetCoordinate():GetPressure()) -local alt=self:_GetMarshalAltitude(stack,flight.case) -local brc=self:GetBRC() -if self.recoverywindow and self.recoverywindow.WIND then -brc=self:GetBRCintoWind() -end -flight.Tcharlie=self:_GetCharlieTime(flight) -local Ccharlie=UTILS.SecondsToClock(flight.Tcharlie) -self:_MarshalCallArrived(flight.onboard,flight.case,brc,alt,Ccharlie,P) -if self.TACANon and(not flight.ai)and flight.difficulty==AIRBOSS.Difficulty.EASY then -local radial=self:GetRadial(flight.case,true,true,true) -if flight.case==1 then -radial=self:GetBRC() -end -local text=string.format("Select TACAN %03d°, channel %d%s (%s)",radial,self.TACANchannel,self.TACANmode,self.TACANmorse) -self:MessageToPlayer(flight,text,nil,"") -end -end -function AIRBOSS:_CollapseMarshalStack(flight,nopattern) -self:F2({flight=flight,nopattern=nopattern}) -local case=flight.case -local stack=flight.flag -if stack<=0 then -self:E(self.lid..string.format("ERROR: Flight %s is has stack value %d<0. Cannot collapse stack!",flight.groupname,stack)) -return -end -self.Tcollapse=timer.getTime() -for _,_flight in pairs(self.Qmarshal)do -local mflight=_flight -if(case==1 and mflight.case==1)then -local mstack=mflight.flag -if mstack>stack then -local newstack=self:_GetFreeStack(mflight.ai,mflight.case,true) -if newstack and newstack %d.",mflight.groupname,mflight.case,mstack,newstack)) -if mflight.ai then -self:_MarshalAI(mflight,newstack) -else -mflight.flag=newstack -local angels=self:_GetAngels(self:_GetMarshalAltitude(newstack,case)) -if mflight.difficulty~=AIRBOSS.Difficulty.HARD then -local text=string.format("descent to stack at Angels %d.",angels) -self:MessageToPlayer(mflight,text,"MARSHAL") -end -mflight.time=timer.getAbsTime() -for _,_sec in pairs(mflight.section)do -local sec=_sec -sec.flag=newstack -sec.time=timer.getAbsTime() -if sec.difficulty~=AIRBOSS.Difficulty.HARD then -local text=string.format("descent to stack at Angels %d.",angels) -self:MessageToPlayer(sec,text,"MARSHAL") -end -end -end -end -end -end -end -if nopattern then -self:T(self.lid..string.format("Flight %s is leaving stack but not going to pattern.",flight.groupname)) -else -local Tmarshal=UTILS.SecondsToClock(timer.getAbsTime()-flight.time) -self:T(self.lid..string.format("Flight %s is leaving marshal after %s and going pattern.",flight.groupname,Tmarshal)) -self:_AddFlightToPatternQueue(flight) -end -flight.flag=-1 -flight.time=timer.getAbsTime() -end -function AIRBOSS:_GetFreeStack(ai,case,empty) -case=case or self.case -if case==1 then -return self:_GetFreeStack_Old(ai,case,empty) -end -local nmaxstacks=100 -if case==1 then -nmaxstacks=self.Nmaxmarshal -end -local stack={} -for i=1,nmaxstacks do -stack[i]=self.NmaxStack -end -local nmax=1 -for _,_flight in pairs(self.Qmarshal)do -local flight=_flight -if flight.case==case then -local n=flight.flag -if n>nmax then -nmax=n -end -if n>0 then -if flight.ai or flight.case>1 then -stack[n]=0 -else -stack[n]=stack[n]-1 -end -else -self:E(string.format("ERROR: Flight %s in marshal stack has stack value <= 0. Stack value is %d.",flight.groupname,n)) -end -end -end -local nfree=nil -if stack[nmax]==0 then -if case==1 then -if nmax>=nmaxstacks then -nfree=nil -else -nfree=nmax+1 -end -else -nfree=nmax+1 -end -elseif stack[nmax]==self.NmaxStack then -self:E(self.lid..string.format("ERROR: Max occupied stack is empty. Should not happen! Nmax=%d, stack[nmax]=%d",nmax,stack[nmax])) -nfree=nmax -else -if ai or empty or case>1 then -nfree=nmax+1 -else -nfree=nmax -end -end -self:I(self.lid..string.format("Returning free stack %s",tostring(nfree))) -return nfree -end -function AIRBOSS:_GetFreeStack_Old(ai,case,empty) -case=case or self.case -local nmaxstacks=100 -if case==1 then -nmaxstacks=self.Nmaxmarshal -end -local stack={} -for i=1,nmaxstacks do -stack[i]=self.NmaxStack -end -for _,_flight in pairs(self.Qmarshal)do -local flight=_flight -if flight.case==case then -local n=flight.flag -if n>0 then -if flight.ai or flight.case>1 then -stack[n]=0 -else -stack[n]=stack[n]-1 -end -else -self:E(string.format("ERROR: Flight %s in marshal stack has stack value <= 0. Stack value is %d.",flight.groupname,n)) -end -end -end -local nfree=nil -for i=1,nmaxstacks do -self:T2(self.lid..string.format("FF Stack[%d]=%d",i,stack[i])) -if ai or empty or case>1 then -if stack[i]==self.NmaxStack then -nfree=i -return i -end -else -if stack[i]>0 then -nfree=i -return i -end -end -end -return nfree -end -function AIRBOSS:_GetFlightUnits(flight,onground) -local inair=true -if onground==true then -inair=false -end -local function countunits(_group,inair) -local group=_group -local units=group:GetUnits() -local n=0 -if units then -for _,_unit in pairs(units)do -local unit=_unit -if unit and unit:IsAlive()then -if inair then -if unit:InAir()then -self:T2(self.lid..string.format("Unit %s is in AIR",unit:GetName())) -n=n+1 -end -else -n=n+1 -end -end -end -end -return n -end -local nunits=countunits(flight.group,inair) -local nsection=0 -for _,sec in pairs(flight.section)do -local secflight=sec -nsection=nsection+countunits(secflight.group,inair) -end -return nunits+nsection,nunits,nsection -end -function AIRBOSS:_GetQueueInfo(queue,case) -local ngroup=0 -local Nunits=0 -for _,_flight in pairs(queue)do -local flight=_flight -if case then -if(flight.case==case)or(case==23 and(flight.case==2 or flight.case==3))then -local ntot,nunits,nsection=self:_GetFlightUnits(flight) -Nunits=Nunits+ntot -if ntot>0 then -ngroup=ngroup+1 -end -end -else -local ntot,nunits,nsection=self:_GetFlightUnits(flight) -Nunits=Nunits+ntot -if ntot>0 then -ngroup=ngroup+1 -end -end -end -return ngroup,Nunits -end -function AIRBOSS:_PrintQueue(queue,name) -local Nqueue,nqueue=self:_GetQueueInfo(queue) -local text=string.format("%s Queue N=%d (#%d), n=%d:",name,Nqueue,#queue,nqueue) -if#queue==0 then -text=text.." empty." -else -for i,_flight in pairs(queue)do -local flight=_flight -local clock=UTILS.SecondsToClock(timer.getAbsTime()-flight.time) -local case=flight.case -local stack=flight.flag -local fuel=flight.group:GetFuelMin()*100 -local ai=tostring(flight.ai) -local lead=flight.seclead -local Nsec=#flight.section -local actype=self:_GetACNickname(flight.actype) -local onboard=flight.onboard -local holding=tostring(flight.holding) -local _,nunits,nsec=self:_GetFlightUnits(flight,false) -text=text..string.format("\n[%d] %s*%d (%s): lead=%s (%d/%d), onboard=%s, flag=%d, case=%d, time=%s, fuel=%d, ai=%s, holding=%s", -i,flight.groupname,nunits,actype,lead,nsec,Nsec,onboard,stack,case,clock,fuel,ai,holding) -if stack>0 then -local alt=UTILS.MetersToFeet(self:_GetMarshalAltitude(stack,case)) -text=text..string.format(" stackalt=%d ft",alt) -end -for j,_element in pairs(flight.elements)do -local element=_element -text=text..string.format("\n (%d) %s (%s): ai=%s, ballcall=%s, recovered=%s", -j,element.onboard,element.unitname,tostring(element.ai),tostring(element.ballcall),tostring(element.recovered)) -end -end -end -self:T(self.lid..text) -end -function AIRBOSS:_CreateFlightGroup(group) -self:T(self.lid..string.format("Creating new flight for group %s of aircraft type %s.",group:GetName(),group:GetTypeName())) -local flight={} -if not self:_InQueue(self.flights,group)then -local groupname=group:GetName() -local human,playername=self:_IsHuman(group) -flight.group=group -flight.groupname=group:GetName() -flight.nunits=#group:GetUnits() -flight.time=timer.getAbsTime() -flight.dist0=group:GetCoordinate():Get2DDistance(self:GetCoordinate()) -flight.flag=-100 -flight.ai=not human -flight.actype=group:GetTypeName() -flight.onboardnumbers=self:_GetOnboardNumbers(group) -flight.seclead=flight.group:GetUnit(1):GetName() -flight.section={} -flight.ballcall=false -flight.refueling=false -flight.holding=nil -flight.name=flight.group:GetUnit(1):GetName() -flight.case=self.case -local text=string.format("Flight elements of group %s:",flight.groupname) -flight.elements={} -local units=group:GetUnits() -for i,_unit in pairs(units)do -local unit=_unit -local element={} -element.unit=unit -element.unitname=unit:GetName() -element.onboard=flight.onboardnumbers[element.unitname] -element.ballcall=false -element.ai=not self:_IsHumanUnit(unit) -element.recovered=nil -text=text..string.format("\n[%d] %s onboard #%s, AI=%s",i,element.unitname,tostring(element.onboard),tostring(element.ai)) -table.insert(flight.elements,element) -end -self:T(self.lid..text) -if flight.ai then -local onboard=flight.onboardnumbers[flight.seclead] -flight.onboard=onboard -else -flight.onboard=self:_GetOnboardNumberPlayer(group) -end -table.insert(self.flights,flight) -else -self:E(self.lid..string.format("ERROR: Flight group %s already exists in self.flights!",group:GetName())) -return nil -end -return flight -end -function AIRBOSS:_NewPlayer(unitname) -local playerunit,playername=self:_GetPlayerUnitAndName(unitname) -if playerunit and playername then -local group=playerunit:GetGroup() -local playerData -playerData=self:_CreateFlightGroup(group) -if playerData then -playerData.unit=playerunit -playerData.unitname=unitname -playerData.name=playername -playerData.callsign=playerData.unit:GetCallsign() -playerData.client=CLIENT:FindByName(unitname,nil,true) -playerData.seclead=playername -playerData.passes=0 -playerData.messages={} -playerData.lastdebrief=playerData.lastdebrief or{} -playerData.attitudemonitor=false -if playerData.trapon==nil then -playerData.trapon=self.trapsheet -end -playerData.difficulty=playerData.difficulty or self.defaultskill -if playerData.subtitles==nil then -playerData.subtitles=true -end -if playerData.showhints==nil then -if playerData.difficulty==AIRBOSS.Difficulty.HARD then -playerData.showhints=false -else -playerData.showhints=true -end -end -playerData.points={} -playerData=self:_InitPlayer(playerData) -self.players[playername]=playerData -self.playerscores[playername]=self.playerscores[playername]or{} -if self.welcome then -self:MessageToPlayer(playerData,string.format("Welcome, %s %s!",playerData.difficulty,playerData.name),string.format("AIRBOSS %s",self.alias),"",5) -end -end -return playerData -end -return nil -end -function AIRBOSS:_InitPlayer(playerData,step) -self:T(self.lid..string.format("Initializing player data for %s callsign %s.",playerData.name,playerData.callsign)) -playerData.step=step or AIRBOSS.PatternStep.UNDEFINED -playerData.groove={} -playerData.debrief={} -playerData.trapsheet={} -playerData.warning=nil -playerData.holding=nil -playerData.refueling=false -playerData.valid=false -playerData.lig=false -playerData.wop=false -playerData.waveoff=false -playerData.wofd=false -playerData.owo=false -playerData.boltered=false -playerData.landed=false -playerData.Tlso=timer.getTime() -playerData.Tgroove=nil -playerData.TIG0=nil -playerData.wire=nil -playerData.flag=-100 -playerData.debriefschedulerID=nil -if playerData.group:GetName():match("Groove")and playerData.passes==0 then -self:MessageToPlayer(playerData,"Group name contains \"Groove\". Happy groove testing.") -playerData.attitudemonitor=true -playerData.step=AIRBOSS.PatternStep.FINAL -self:_AddFlightToPatternQueue(playerData) -self.dTstatus=0.1 -end -return playerData -end -function AIRBOSS:_GetFlightFromGroupInQueue(group,queue) -if group then -local name=group:GetName() -for i,_flight in pairs(queue)do -local flight=_flight -if flight.groupname==name then -return flight,i -end -end -self:T2(self.lid..string.format("WARNING: Flight group %s could not be found in queue.",name)) -end -self:T2(self.lid..string.format("WARNING: Flight group could not be found in queue. Group is nil!")) -return nil,nil -end -function AIRBOSS:_GetFlightElement(unitname) -local unit=UNIT:FindByName(unitname) -if unit then -local flight=self:_GetFlightFromGroupInQueue(unit:GetGroup(),self.flights) -if flight then -for i,_element in pairs(flight.elements)do -local element=_element -if element.unit:GetName()==unitname then -return element,i,flight -end -end -self:T2(self.lid..string.format("WARNING: Flight element %s could not be found in flight group.",unitname,flight.groupname)) -end -end -return nil,nil,nil -end -function AIRBOSS:_RemoveFlightElement(unitname) -local element,idx,flight=self:_GetFlightElement(unitname) -if idx then -table.remove(flight.elements,idx) -return true -else -self:T("WARNING: Flight element could not be removed from flight group. Index=nil!") -return nil -end -end -function AIRBOSS:_InQueue(queue,group) -local name=group:GetName() -for _,_flight in pairs(queue)do -local flight=_flight -if name==flight.groupname then -return true -end -end -return false -end -function AIRBOSS:_RemoveDeadFlightGroups() -for i=#self.flight,1,-1 do -local flight=self.flights[i] -if not flight.group:IsAlive()then -self:T(string.format("Removing dead flight group %s from ALL flights table.",flight.groupname)) -table.remove(self.flights,i) -end -end -for i=#self.Qmarshal,1,-1 do -local flight=self.Qmarshal[i] -if not flight.group:IsAlive()then -self:T(string.format("Removing dead flight group %s from Marshal Queue table.",flight.groupname)) -table.remove(self.Qmarshal,i) -end -end -for i=#self.Qpattern,1,-1 do -local flight=self.Qpattern[i] -if not flight.group:IsAlive()then -self:T(string.format("Removing dead flight group %s from Pattern Queue table.",flight.groupname)) -table.remove(self.Qpattern,i) -end -end -end -function AIRBOSS:_GetLeadFlight(flight) -local lead=flight -if flight.name~=flight.seclead then -lead=self.players[flight.seclead] -end -return lead -end -function AIRBOSS:_CheckSectionRecovered(flight) -if flight==nil then -return true -end -local lead=self:_GetLeadFlight(flight) -for _,_element in pairs(lead.elements)do -local element=_element -if not element.recovered then -return false -end -end -for _,_section in pairs(lead.section)do -local sectionmember=_section -for _,_element in pairs(sectionmember.elements)do -local element=_element -if not element.recovered then -return false -end -end -end -self:_RemoveFlightFromQueue(self.Qpattern,lead) -if self:_InQueue(self.Qmarshal,lead.group)then -self:E(self.lid..string.format("ERROR: lead flight group %s should not be in marshal queue",lead.groupname)) -self:_RemoveFlightFromMarshalQueue(lead,true) -end -if self:_InQueue(self.Qwaiting,lead.group)then -self:E(self.lid..string.format("ERROR: lead flight group %s should not be in pattern queue",lead.groupname)) -self:_RemoveFlightFromQueue(self.Qwaiting,lead) -end -return true -end -function AIRBOSS:_AddFlightToPatternQueue(flight) -table.insert(self.Qpattern,flight) -flight.flag=-1 -flight.time=timer.getAbsTime() -flight.recovered=false -for _,elem in pairs(flight.elements)do -elem.recoverd=false -end -for _,sec in pairs(flight.section)do -sec.flag=-1 -sec.time=timer.getAbsTime() -for _,elem in pairs(sec.elements)do -elem.recoverd=false -end -end -end -function AIRBOSS:_RecoveredElement(unit) -local element,idx,flight=self:_GetFlightElement(unit:GetName()) -if element then -element.recovered=true -end -return flight -end -function AIRBOSS:_RemoveFlightFromMarshalQueue(flight,nopattern) -local removed,idx=self:_RemoveFlightFromQueue(self.Qmarshal,flight) -if removed then -flight.holding=nil -self:_CollapseMarshalStack(flight,nopattern) -if flight.case==1 and#self.Qwaiting>0 then -local nextflight=self.Qwaiting[1] -local freestack=self:_GetFreeStack(nextflight.ai) -if nextflight.ai then -self:_MarshalAI(nextflight,freestack) -else -self:_MarshalPlayer(nextflight,freestack) -end -self:_RemoveFlightFromQueue(self.Qwaiting,nextflight) -end -end -return removed,idx -end -function AIRBOSS:_RemoveFlightFromQueue(queue,flight) -for i,_flight in pairs(queue)do -local qflight=_flight -if qflight.groupname==flight.groupname then -self:T(self.lid..string.format("Removing flight group %s from queue.",flight.groupname)) -table.remove(queue,i) -return true,i -end -end -return false,nil -end -function AIRBOSS:_RemoveUnitFromFlight(unit) -if unit and unit:IsInstanceOf("UNIT")then -local group=unit:GetGroup() -if group then -local flight=self:_GetFlightFromGroupInQueue(group,self.flights) -if flight then -local removed=self:_RemoveFlightElement(unit:GetName()) -if removed then -local _,nunits=self:_GetFlightUnits(flight,not flight.ai) -local nelements=#flight.elements -self:T(self.lid..string.format("Removed unit %s: nunits=%d, nelements=%d",unit:GetName(),nunits,nelements)) -if nunits==0 or nelements==0 then -self:_RemoveFlight(flight) -end -end -end -end -end -end -function AIRBOSS:_RemoveFlightFromSection(flight) -if flight.name~=flight.seclead then -local lead=self.players[flight.seclead] -if lead then -for i,sec in pairs(lead.section)do -local sectionmember=sec -if sectionmember.name==flight.name then -table.remove(lead.section,i) -break -end -end -end -end -end -function AIRBOSS:_UpdateFlightSection(flight) -if flight.seclead==flight.name then -if#flight.section>=1 then -local newlead=flight.section[1] -newlead.seclead=newlead.name -for i=2,#flight.section do -local member=flight.section[i] -table.insert(newlead.section,member) -member.seclead=newlead.name -end -end -flight.section={} -else -self:_RemoveFlightFromSection(flight) -end -end -function AIRBOSS:_RemoveFlight(flight,completely) -self:F(self.lid..string.format("Removing flight %s, ai=%s completely=%s.",tostring(flight.groupname),tostring(flight.ai),tostring(completely))) -self:_RemoveFlightFromMarshalQueue(flight,true) -self:_RemoveFlightFromQueue(self.Qpattern,flight) -self:_RemoveFlightFromQueue(self.Qwaiting,flight) -self:_RemoveFlightFromQueue(self.Qspinning,flight) -if flight.ai then -self:_RemoveFlightFromQueue(self.flights,flight) -else -local grades=self.playerscores[flight.name] -if grades and#grades>0 then -while#grades>0 and grades[#grades].finalscore==nil do -table.remove(grades,#grades) -end -end -if completely then -self:_UpdateFlightSection(flight) -self:_RemoveFlightFromQueue(self.flights,flight) -local playerdata=self.players[flight.name] -if playerdata then -self:I(self.lid..string.format("Removing player %s completely.",flight.name)) -self.players[flight.name]=nil -end -flight=nil -else -self:_SetPlayerStep(flight,AIRBOSS.PatternStep.UNDEFINED) -for _,sectionmember in pairs(flight.section)do -self:_SetPlayerStep(sectionmember,AIRBOSS.PatternStep.UNDEFINED) -self:_RemoveFlightFromQueue(self.Qspinning,sectionmember) -end -self:_RemoveFlightFromSection(flight) -end -end -end -function AIRBOSS:_CheckPlayerStatus() -for _playerName,_playerData in pairs(self.players)do -local playerData=_playerData -if playerData then -local unit=playerData.unit -if unit and unit:IsAlive()then -if unit:IsInZone(self.zoneCCA)then -if playerData.attitudemonitor then -self:_AttitudeMonitor(playerData) -end -self:_CheckPlayerPatternDistance(playerData) -self:_CheckFoulDeck(playerData) -if playerData.step==AIRBOSS.PatternStep.UNDEFINED then -elseif playerData.step==AIRBOSS.PatternStep.REFUELING then -elseif playerData.step==AIRBOSS.PatternStep.SPINNING then -self:_Spinning(playerData) -elseif playerData.step==AIRBOSS.PatternStep.HOLDING then -self:_Holding(playerData) -elseif playerData.step==AIRBOSS.PatternStep.WAITING then -self:_Waiting(playerData) -elseif playerData.step==AIRBOSS.PatternStep.COMMENCING then -self:_Commencing(playerData,true) -elseif playerData.step==AIRBOSS.PatternStep.BOLTER then -self:_BolterPattern(playerData) -elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then -self:_Platform(playerData) -elseif playerData.step==AIRBOSS.PatternStep.ARCIN then -self:_ArcInTurn(playerData) -elseif playerData.step==AIRBOSS.PatternStep.ARCOUT then -self:_ArcOutTurn(playerData) -elseif playerData.step==AIRBOSS.PatternStep.DIRTYUP then -self:_DirtyUp(playerData) -elseif playerData.step==AIRBOSS.PatternStep.BULLSEYE then -self:_Bullseye(playerData) -elseif playerData.step==AIRBOSS.PatternStep.INITIAL then -self:_Initial(playerData) -elseif playerData.step==AIRBOSS.PatternStep.BREAKENTRY then -self:_BreakEntry(playerData) -elseif playerData.step==AIRBOSS.PatternStep.EARLYBREAK then -self:_Break(playerData,AIRBOSS.PatternStep.EARLYBREAK) -elseif playerData.step==AIRBOSS.PatternStep.LATEBREAK then -self:_Break(playerData,AIRBOSS.PatternStep.LATEBREAK) -elseif playerData.step==AIRBOSS.PatternStep.ABEAM then -self:_Abeam(playerData) -elseif playerData.step==AIRBOSS.PatternStep.NINETY then -self:_CheckForLongDownwind(playerData) -self:_Ninety(playerData) -elseif playerData.step==AIRBOSS.PatternStep.WAKE then -self:_Wake(playerData) -elseif playerData.step==AIRBOSS.PatternStep.EMERGENCY then -self:_Final(playerData,true) -elseif playerData.step==AIRBOSS.PatternStep.FINAL then -self:_Final(playerData) -elseif playerData.step==AIRBOSS.PatternStep.GROOVE_XX or -playerData.step==AIRBOSS.PatternStep.GROOVE_IM or -playerData.step==AIRBOSS.PatternStep.GROOVE_IC or -playerData.step==AIRBOSS.PatternStep.GROOVE_AR or -playerData.step==AIRBOSS.PatternStep.GROOVE_AL or -playerData.step==AIRBOSS.PatternStep.GROOVE_LC or -playerData.step==AIRBOSS.PatternStep.GROOVE_IW then -self:_Groove(playerData) -elseif playerData.step==AIRBOSS.PatternStep.DEBRIEF then -playerData.debriefschedulerID=self:ScheduleOnce(5,self._Debrief,self,playerData) -playerData.step=AIRBOSS.PatternStep.UNDEFINED -else -self:E(self.lid..string.format("ERROR: Unknown player step %s. Please report!",tostring(playerData.step))) -end -self:_CheckMissedStepOnEntry(playerData) -else -self:T2(self.lid.."WARNING: Player unit not inside the CCA!") -end -else -self:T(self.lid.."WARNING: Player unit is not alive!") -end -end -end -end -function AIRBOSS:_CheckMissedStepOnEntry(playerData) -local rightcase=playerData.case>1 -local rightqueue=self:_InQueue(self.Qpattern,playerData.group) -local rightflag=playerData.flag~=-42 -local step=playerData.step -local missedstep=step==AIRBOSS.PatternStep.PLATFORM or step==AIRBOSS.PatternStep.ARCIN or step==AIRBOSS.PatternStep.ARCOUT or step==AIRBOSS.PatternStep.DIRTYUP -if rightcase and rightqueue and rightflag then -local zone=nil -if playerData.case==2 and missedstep then -zone=self:_GetZoneInitial(playerData.case) -elseif playerData.case==3 and missedstep then -zone=self:_GetZoneBullseye(playerData.case) -end -if zone then -local inzone=playerData.unit:IsInZone(zone) -local relheading=self:_GetRelativeHeading(playerData.unit,false) -if inzone and math.abs(relheading)<60 then -local text=string.format("you missed an important step in the pattern!\nYour next step would have been %s.",playerData.step) -self:MessageToPlayer(playerData,text,"AIRBOSS",nil,5) -if playerData.case==2 then -playerData.step=AIRBOSS.PatternStep.INITIAL -elseif playerData.case==3 then -playerData.step=AIRBOSS.PatternStep.BULLSEYE -end -playerData.flag=-42 -end -end -end -end -function AIRBOSS:_SetTimeInGroove(playerData) -if playerData.TIG0 then -playerData.Tgroove=timer.getTime()-playerData.TIG0 -else -playerData.Tgroove=999 -end -end -function AIRBOSS:_GetTimeInGroove(playerData) -local Tgroove=999 -if playerData.TIG0 then -Tgroove=timer.getTime()-playerData.TIG0 -end -return Tgroove -end -function AIRBOSS:OnEventBirth(EventData) -self:F3({eventbirth=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event BIRTH!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event BIRTH!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T(self.lid.."BIRTH: unit = "..tostring(EventData.IniUnitName)) -self:T(self.lid.."BIRTH: group = "..tostring(EventData.IniGroupName)) -self:T(self.lid.."BIRTH: player = "..tostring(_playername)) -if _unit and _playername then -local _uid=_unit:GetID() -local _group=_unit:GetGroup() -local _callsign=_unit:GetCallsign() -local text=string.format("Pilot %s, callsign %s entered unit %s of group %s.",_playername,_callsign,_unitName,_group:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,5):ToAllIf(self.Debug) -local rightaircraft=self:_IsCarrierAircraft(_unit) -if rightaircraft==false then -local text=string.format("Player aircraft type %s not supported by AIRBOSS class.",_unit:GetTypeName()) -MESSAGE:New(text,30):ToAllIf(self.Debug) -self:T2(self.lid..text) -return -end -if self:GetCoalition()~=_unit:GetCoalition()then -local text=string.format("Player entered aircraft of other coalition.") -MESSAGE:New(text,30):ToAllIf(self.Debug) -self:T(self.lid..text) -return -end -self:_AddF10Commands(_unitName) -self:ScheduleOnce(1,self._NewPlayer,self,_unitName) -end -end -function AIRBOSS:OnEventLand(EventData) -self:F3({eventland=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event LAND!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event LAND!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T(self.lid.."LAND: unit = "..tostring(EventData.IniUnitName)) -self:T(self.lid.."LAND: group = "..tostring(EventData.IniGroupName)) -self:T(self.lid.."LAND: player = "..tostring(_playername)) -local airbase=EventData.Place -if airbase==nil then -return -end -local airbasename=tostring(airbase:GetName()) -if airbasename==self.airbase:GetName()then -local stern=self:_GetSternCoord() -local zoneCarrier=self:_GetZoneCarrierBox() -if _unit and _playername then -local _uid=_unit:GetID() -local _group=_unit:GetGroup() -local _callsign=_unit:GetCallsign() -local text=string.format("Player %s, callsign %s unit %s (ID=%d) of group %s landed at airbase %s",_playername,_callsign,_unitName,_uid,_group:GetName(),airbasename) -self:T(self.lid..text) -MESSAGE:New(text,5,"DEBUG"):ToAllIf(self.Debug) -local playerData=self.players[_playername] -if playerData==nil then -self:E(self.lid..string.format("ERROR: playerData nil in landing event. unit=%s player=%s",tostring(_unitName),tostring(_playername))) -return -end -if _unit:IsInZone(zoneCarrier)then -if not playerData.valid then -local text=string.format("you missed at least one important step in the pattern!\nYour next step would have been %s.\nThis pass is INVALID.",playerData.step) -self:MessageToPlayer(playerData,text,"AIRBOSS",nil,30,true,5) -self:_RemoveFlightFromMarshalQueue(playerData,true) -self:_RemoveFlightFromQueue(self.Qpattern,playerData) -self:_RemoveFlightFromQueue(self.Qwaiting,playerData) -self:_RemoveFlightFromQueue(self.Qspinning,playerData) -self:_InitPlayer(playerData) -return -end -if playerData.landed then -self:E(self.lid..string.format("Player %s just landed a second time.",_playername)) -else -playerData.landed=true -playerData.attitudemonitor=false -local coord=playerData.unit:GetCoordinate() -local X,Z,rho,phi=self:_GetDistances(_unit) -local dist=coord:Get2DDistance(stern) -if self.Debug and false then -local lp=coord:MarkToAll("Landing coord.") -coord:SmokeGreen() -end -self:_SetTimeInGroove(playerData) -local text=string.format("Player %s AC type %s landed at dist=%.1f m. Tgroove=%.1f sec.",playerData.name,playerData.actype,dist,self:_GetTimeInGroove(playerData)) -text=text..string.format(" X=%.1f m, Z=%.1f m, rho=%.1f m.",X,Z,rho) -self:T(self.lid..text) -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -self:RadioTransmission(self.LSORadio,self.LSOCall.IDLE,false,1,nil,true) -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.DEBRIEF) -else -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.UNDEFINED) -self:ScheduleOnce(1,self._Trapped,self,playerData) -end -end -else -if playerData then -self:E(self.lid..string.format("Player %s did not land in carrier box zone. Maybe in the water near the carrier?",playerData.name)) -end -end -else -if self.carriertype~=AIRBOSS.CarrierType.TARAWA then -local coord=EventData.IniUnit:GetCoordinate() -local dist=coord:Get2DDistance(self:GetCoordinate()) -local wire=self:_GetWire(coord,0) -local _type=EventData.IniUnit:GetTypeName() -local text=string.format("AI unit %s of type %s landed at dist=%.1f m. Trapped wire=%d.",_unitName,_type,dist,wire) -self:T(self.lid..text) -end -local flight=self:_RecoveredElement(EventData.IniUnit) -self:_CheckSectionRecovered(flight) -end -end -end -function AIRBOSS:OnEventEngineShutdown(EventData) -self:F3({eventengineshutdown=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event ENGINESHUTDOWN!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event ENGINESHUTDOWN!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T3(self.lid.."ENGINESHUTDOWN: unit = "..tostring(EventData.IniUnitName)) -self:T3(self.lid.."ENGINESHUTDOWN: group = "..tostring(EventData.IniGroupName)) -self:T3(self.lid.."ENGINESHUTDOWN: player = "..tostring(_playername)) -if _unit and _playername then -self:T(self.lid..string.format("Player %s shut down its engines!",_playername)) -else -self:T(self.lid..string.format("AI unit %s shut down its engines!",_unitName)) -local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights) -if flight and flight.ai then -local recovered=self:_CheckSectionRecovered(flight) -if recovered then -self:T(self.lid..string.format("AI group %s completely recovered. Despawning group after engine shutdown event as requested in 5 seconds.",tostring(EventData.IniGroupName))) -self:_RemoveFlight(flight) -local istanker=self.tanker and self.tanker.tanker:GetName()==EventData.IniGroupName -local isawacs=self.awacs and self.awacs.tanker:GetName()==EventData.IniGroupName -if self.despawnshutdown and not(istanker or isawacs)then -EventData.IniGroup:Destroy(nil,5) -end -end -end -end -end -function AIRBOSS:OnEventTakeoff(EventData) -self:F3({eventtakeoff=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event TAKEOFF!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event TAKEOFF!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T3(self.lid.."TAKEOFF: unit = "..tostring(EventData.IniUnitName)) -self:T3(self.lid.."TAKEOFF: group = "..tostring(EventData.IniGroupName)) -self:T3(self.lid.."TAKEOFF: player = "..tostring(_playername)) -local airbase=EventData.Place -local airbasename="unknown" -if airbase then -airbasename=airbase:GetName() -end -if airbasename==self.airbase:GetName()then -if _unit and _playername then -self:T(self.lid..string.format("Player %s took off at %s!",_playername,airbasename)) -else -self:T2(self.lid..string.format("AI unit %s took off at %s!",_unitName,airbasename)) -local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights) -if flight then -for _,elem in pairs(flight.elements)do -local element=elem -element.ballcall=false -element.recovered=nil -end -end -end -end -end -function AIRBOSS:OnEventCrash(EventData) -self:F3({eventcrash=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event CRASH!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event CRASH!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T3(self.lid.."CRASH: unit = "..tostring(EventData.IniUnitName)) -self:T3(self.lid.."CRASH: group = "..tostring(EventData.IniGroupName)) -self:T3(self.lid.."CARSH: player = "..tostring(_playername)) -if _unit and _playername then -self:T(self.lid..string.format("Player %s crashed!",_playername)) -local flight=self.players[_playername] -if flight then -self:_RemoveFlight(flight,true) -end -else -self:T2(self.lid..string.format("AI unit %s crashed!",EventData.IniUnitName)) -self:_RemoveUnitFromFlight(EventData.IniUnit) -end -end -function AIRBOSS:OnEventEjection(EventData) -self:F3({eventland=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event EJECTION!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event EJECTION!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T3(self.lid.."EJECT: unit = "..tostring(EventData.IniUnitName)) -self:T3(self.lid.."EJECT: group = "..tostring(EventData.IniGroupName)) -self:T3(self.lid.."EJECT: player = "..tostring(_playername)) -if _unit and _playername then -self:T(self.lid..string.format("Player %s ejected!",_playername)) -local flight=self.players[_playername] -if flight then -self:_RemoveFlight(flight,true) -end -else -self:T(self.lid..string.format("AI unit %s ejected!",EventData.IniUnitName)) -self:_RemoveUnitFromFlight(EventData.IniUnit) -local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights) -self:_CheckSectionRecovered(flight) -end -end -function AIRBOSS:OnEventRemoveUnit(EventData) -self:F3({eventland=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event REMOVEUNIT!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event REMOVEUNIT!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T3(self.lid.."EJECT: unit = "..tostring(EventData.IniUnitName)) -self:T3(self.lid.."EJECT: group = "..tostring(EventData.IniGroupName)) -self:T3(self.lid.."EJECT: player = "..tostring(_playername)) -if _unit and _playername then -self:T(self.lid..string.format("Player %s removed!",_playername)) -local flight=self.players[_playername] -if flight then -self:_RemoveFlight(flight,true) -end -else -self:T(self.lid..string.format("AI unit %s removed!",EventData.IniUnitName)) -self:_RemoveUnitFromFlight(EventData.IniUnit) -local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights) -self:_CheckSectionRecovered(flight) -end -end -function AIRBOSS:_PlayerLeft(EventData) -self:F3({eventleave=EventData}) -if EventData==nil then -self:E(self.lid.."ERROR: EventData=nil in event PLAYERLEFTUNIT!") -self:E(EventData) -return -end -if EventData.IniUnit==nil then -self:E(self.lid.."ERROR: EventData.IniUnit=nil in event PLAYERLEFTUNIT!") -self:E(EventData) -return -end -local _unitName=EventData.IniUnitName -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -self:T3(self.lid.."PLAYERLEAVEUNIT: unit = "..tostring(EventData.IniUnitName)) -self:T3(self.lid.."PLAYERLEAVEUNIT: group = "..tostring(EventData.IniGroupName)) -self:T3(self.lid.."PLAYERLEAVEUNIT: player = "..tostring(_playername)) -if _unit and _playername then -self:T(self.lid..string.format("Player %s left unit %s!",_playername,_unitName)) -local flight=self.players[_playername] -if flight then -self:_RemoveFlight(flight,true) -end -end -end -function AIRBOSS:OnEventMissionEnd(EventData) -self:T3(self.lid.."Mission Ended") -end -function AIRBOSS:_Spinning(playerData) -local SpinIt={} -SpinIt.name="Spinning" -SpinIt.Xmin=-UTILS.NMToMeters(6) -SpinIt.Xmax=UTILS.NMToMeters(5) -SpinIt.Zmin=-UTILS.NMToMeters(6) -SpinIt.Zmax=UTILS.NMToMeters(2) -SpinIt.LimitXmin=-100 -SpinIt.LimitXmax=nil -SpinIt.LimitZmin=-UTILS.NMToMeters(1) -SpinIt.LimitZmax=nil -local X,Z,rho,phi=self:_GetDistances(playerData.unit) -if self:_CheckLimits(X,Z,SpinIt)then -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.INITIAL) -self:_RemoveFlightFromQueue(self.Qspinning,playerData) -end -end -function AIRBOSS:_Waiting(playerData) -local radius=UTILS.NMToMeters(10) -local zone=ZONE_RADIUS:New("Carrier 10 NM Zone",self.carrier:GetVec2(),radius) -local inzone=playerData.unit:IsInZone(zone) -local Twaiting=timer.getAbsTime()-playerData.time -if inzone and Twaiting>3*60 and not playerData.warning then -local text=string.format("You are supposed to wait outside the 10 NM zone.") -self:MessageToPlayer(playerData,text,"AIRBOSS") -playerData.warning=true -end -if inzone==false and playerData.warning==true then -playerData.warning=nil -end -end -function AIRBOSS:_Holding(playerData) -local unit=playerData.unit -local stack=playerData.flag -if stack<=0 then -local text=string.format("ERROR: player %s in step %s is holding but has stack=%s (<=0)",playerData.name,playerData.step,tostring(stack)) -self:E(self.lid..text) -end -local patternalt=self:_GetMarshalAltitude(stack,playerData.case) -local playeralt=unit:GetAltitude() -local zoneHolding=self:_GetZoneHolding(playerData.case,stack) -if zoneHolding==nil then -self:E(self.lid.."ERROR: zoneHolding is nil!") -self:E({playerData=playerData}) -return -end -local inholdingzone=unit:IsInZone(zoneHolding) -local altdiff=playeralt-patternalt -local altgood=UTILS.FeetToMeters(500) -if playerData.difficulty==AIRBOSS.Difficulty.HARD then -altgood=UTILS.FeetToMeters(200) -elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then -altgood=UTILS.FeetToMeters(350) -elseif playerData.difficulty==AIRBOSS.Difficulty.EASY then -altgood=UTILS.FeetToMeters(500) -end -local altback=altgood*0.5 -local justcollapsed=false -if self.Tcollapse then -local dT=timer.getTime()-self.Tcollapse -if dT<=90 then -justcollapsed=true -end -end -local goodalt=math.abs(altdiff)altgood then -if not playerData.warning then -text=text..string.format("You left your assigned altitude. Descent to angels %d.",angels) -playerData.warning=true -end -elseif altdiff<-altgood then -if not playerData.warning then -text=text..string.format("You left your assigned altitude. Climb to angels %d.",angels) -playerData.warning=true -end -end -end -if playerData.warning and math.abs(altdiff)<=altback then -text=text..string.format("Altitude is looking good again.") -playerData.warning=nil -end -elseif playerData.holding==false then -if inholdingzone then -text=text..string.format("You are back in the holding zone. Now stay there!") -playerData.holding=true -else -self:T3("Player still outside the holding zone. What are you doing man?!") -end -elseif playerData.holding==nil then -if inholdingzone then -playerData.holding=true -text=text..string.format("You arrived at the holding zone.") -if goodalt then -text=text..string.format(" Altitude is good.") -else -if altdiff<0 then -text=text..string.format(" But you're too low.") -else -text=text..string.format(" But you're too high.") -end -text=text..string.format("\nCurrently assigned altitude is %d ft.",UTILS.MetersToFeet(patternalt)) -playerData.warning=true -end -else -self:T3("Waiting for player to arrive in the holding zone.") -end -end -if playerData.showhints then -self:MessageToPlayer(playerData,text,"MARSHAL") -end -end -function AIRBOSS:_Commencing(playerData,zonecheck) -if zonecheck then -local zoneCommence=self:_GetZoneCommence(playerData.case,playerData.flag) -local inzone=playerData.unit:IsInZone(zoneCommence) -if not inzone then -if timer.getAbsTime()-playerData.time>180 then -self:_MarshalCallClearedForRecovery(playerData.onboard,playerData.case) -playerData.time=timer.getAbsTime() -end -return -end -end -self:_RemoveFlightFromMarshalQueue(playerData) -self:_InitPlayer(playerData) -if playerData.difficulty~=AIRBOSS.Difficulty.HARD then -local text="" -if playerData.case==1 then -text=text.."Proceed to initial." -else -text=text.."Descent to platform." -if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.showhints then -text=text.." VSI 4000 ft/min until you reach 5000 ft." -end -end -self:MessageToPlayer(playerData,text,"MARSHAL") -end -local nextstep -if playerData.case==1 then -nextstep=AIRBOSS.PatternStep.INITIAL -else -nextstep=AIRBOSS.PatternStep.PLATFORM -end -self:_SetPlayerStep(playerData,nextstep) -for i,_flight in pairs(playerData.section)do -local flight=_flight -self:_Commencing(flight,false) -end -end -function AIRBOSS:_Initial(playerData) -local inzone=playerData.unit:IsInZone(self:_GetZoneInitial(playerData.case)) -local relheading=self:_GetRelativeHeading(playerData.unit,false) -local altitude=playerData.unit:GetAltitude() -if inzone and math.abs(relheading)<60 and altitude<=self.initialmaxalt then -if playerData.showhints then -local hint=string.format("Initial") -if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then -if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then -hint=hint.." - Hook down, SAS on, Wing Sweep 68°!" -else -hint=hint.." - Hook down!" -end -end -self:MessageToPlayer(playerData,hint,"MARSHAL") -end -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.BREAKENTRY) -return true -end -return false -end -function AIRBOSS:_CheckCorridor(playerData) -local validzone=self:_GetZoneCorridor(playerData.case) -local invalid=playerData.unit:IsNotInZone(validzone) -if invalid and(not playerData.warning)then -self:MessageToPlayer(playerData,"you left the approach corridor!","AIRBOSS") -playerData.warning=true -end -if(not invalid)and playerData.warning then -self:MessageToPlayer(playerData,"you're back in the approach corridor.","AIRBOSS") -playerData.warning=false -end -end -function AIRBOSS:_Platform(playerData) -self:_CheckCorridor(playerData) -local inzone=playerData.unit:IsInZone(self:_GetZonePlatform(playerData.case)) -if inzone then -self:_PlayerHint(playerData) -local nextstep -if math.abs(self.holdingoffset)>0 and playerData.case>1 then -nextstep=AIRBOSS.PatternStep.ARCIN -else -if playerData.case==2 then -nextstep=AIRBOSS.PatternStep.INITIAL -elseif playerData.case==3 then -nextstep=AIRBOSS.PatternStep.DIRTYUP -end -end -self:_SetPlayerStep(playerData,nextstep) -end -end -function AIRBOSS:_ArcInTurn(playerData) -self:_CheckCorridor(playerData) -local inzone=playerData.unit:IsInZone(self:_GetZoneArcIn(playerData.case)) -if inzone then -self:_PlayerHint(playerData) -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.ARCOUT) -end -end -function AIRBOSS:_ArcOutTurn(playerData) -self:_CheckCorridor(playerData) -local inzone=playerData.unit:IsInZone(self:_GetZoneArcOut(playerData.case)) -if inzone then -self:_PlayerHint(playerData) -local nextstep -if playerData.case==3 then -nextstep=AIRBOSS.PatternStep.DIRTYUP -else -nextstep=AIRBOSS.PatternStep.INITIAL -end -self:_SetPlayerStep(playerData,nextstep) -end -end -function AIRBOSS:_DirtyUp(playerData) -self:_CheckCorridor(playerData) -local inzone=playerData.unit:IsInZone(self:_GetZoneDirtyUp(playerData.case)) -if inzone then -self:_PlayerHint(playerData) -if playerData.actype==AIRBOSS.AircraftCarrier.HORNET or playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then -local callsay=self:_NewRadioCall(self.MarshalCall.SAYNEEDLES,nil,nil,5,playerData.onboard) -local callfly=self:_NewRadioCall(self.MarshalCall.FLYNEEDLES,nil,nil,5,playerData.onboard) -self:RadioTransmission(self.MarshalRadio,callsay,false,55,nil,true) -self:RadioTransmission(self.MarshalRadio,callfly,false,60,nil,true) -end -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.BULLSEYE) -end -end -function AIRBOSS:_Bullseye(playerData) -self:_CheckCorridor(playerData) -local inzone=playerData.unit:IsInZone(self:_GetZoneBullseye(playerData.case)) -local relheading=self:_GetRelativeHeading(playerData.unit,true) -if inzone and math.abs(relheading)<60 then -self:_PlayerHint(playerData) -if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then -self:RadioTransmission(self.LSORadio,self.LSOCall.EXPECTSPOT75,nil,nil,nil,true) -end -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.GROOVE_XX) -end -end -function AIRBOSS:_BolterPattern(playerData) -local X,Z,rho,phi=self:_GetDistances(playerData.unit) -local Bolter={} -Bolter.name="Bolter Pattern" -Bolter.Xmin=-UTILS.NMToMeters(5) -Bolter.Xmax=UTILS.NMToMeters(3) -Bolter.Zmin=-UTILS.NMToMeters(5) -Bolter.Zmax=UTILS.NMToMeters(1) -Bolter.LimitXmin=100 -Bolter.LimitXmax=nil -Bolter.LimitZmin=nil -Bolter.LimitZmax=nil -if self:_CheckLimits(X,Z,Bolter)then -local nextstep -if playerData.case<3 then -nextstep=AIRBOSS.PatternStep.ABEAM -else -nextstep=AIRBOSS.PatternStep.BULLSEYE -end -self:_SetPlayerStep(playerData,nextstep) -end -end -function AIRBOSS:_BreakEntry(playerData) -local X,Z=self:_GetDistances(playerData.unit) -if self:_CheckAbort(X,Z,self.BreakEntry)then -self:_AbortPattern(playerData,X,Z,self.BreakEntry,true) -return -end -if self:_CheckLimits(X,Z,self.BreakEntry)then -self:_PlayerHint(playerData) -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.EARLYBREAK) -end -end -function AIRBOSS:_Break(playerData,part) -local X,Z=self:_GetDistances(playerData.unit) -local breakpoint=self.BreakEarly -if part==AIRBOSS.PatternStep.LATEBREAK then -breakpoint=self.BreakLate -end -if self:_CheckAbort(X,Z,breakpoint)then -self:_AbortPattern(playerData,X,Z,breakpoint,true) -return -end -local tooclose=false -if part==AIRBOSS.PatternStep.LATEBREAK then -local close=0.8 -if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then -close=0.5 -end -if X<0 and Z90 and self:_CheckLimits(X,Z,self.Wake)then -self:MessageToPlayer(playerData,"you are already at the wake and have not passed the 90. Turn faster next time!","LSO") -self:RadioTransmission(self.LSORadio,self.LSOCall.DEPARTANDREENTER,nil,nil,nil,true) -playerData.wop=true -self:_AddToDebrief(playerData,"Overshoot at wake - Pattern Waveoff!") -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.DEBRIEF) -end -end -function AIRBOSS:_Wake(playerData) -local X,Z=self:_GetDistances(playerData.unit) -if self:_CheckAbort(X,Z,self.Wake)then -self:_AbortPattern(playerData,X,Z,self.Wake,true) -return -end -if self:_CheckLimits(X,Z,self.Wake)then -self:_PlayerHint(playerData) -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.FINAL) -end -end -function AIRBOSS:_GetGrooveData(playerData) -local X,Z=self:_GetDistances(playerData.unit) -local stern=self:_GetSternCoord() -local rho=stern:Get2DDistance(playerData.unit:GetCoordinate()) -local astern=X=RAR and rho<=RIC and not playerData.waveoff then -local waveoff=self:_CheckWaveOff(glideslopeError,lineupError,AoA,playerData) -if waveoff then -self:T3(self.lid..string.format("Waveoff distance rho=%.1f m",rho)) -self:RadioTransmission(self.LSORadio,self.LSOCall.WAVEOFF,nil,nil,nil,true) -playerData.Tlso=timer.getTime() -playerData.waveoff=true -return -end -end -groovedata.Step=playerData.step -if rho>=RAR and rho=RAR and rho<=RIM then -if gd.LUE>0.22 and lineupError<-0.22 then -env.info" Drift Right across centre ==> DR-" -gd.Drift=" DR" -self:T(self.lid..string.format("Got Drift Right across centre step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError)) -elseif gd.LUE<-0.22 and lineupError>0.22 then -env.info" Drift Left ==> DL-" -gd.Drift=" DL" -self:T(self.lid..string.format("Got Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError)) -elseif gd.LUE>0.13 and lineupError<-0.14 then -env.info" Little Drift Right across centre ==> (DR-)" -gd.Drift=" (DR)" -self:T(self.lid..string.format("Got Little Drift Right across centre at step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError)) -elseif gd.LUE<-0.13 and lineupError>0.14 then -env.info" Little Drift Left across centre ==> (DL-)" -gd.Drift=" (DL)" -self:E(self.lid..string.format("Got Little Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError)) -end -end -if math.abs(lineupError)>math.abs(gd.LUE)then -self:T(self.lid..string.format("Got bigger LUE at step %s, d=%.3f: LUE %.3f>%.3f",gs,d,lineupError,gd.LUE)) -gd.LUE=lineupError -end -if gd.GSE>0.4 and glideslopeError<-0.3 then -gd.FlyThrough="\\" -self:T(self.lid..string.format("Got Fly through DOWN at step %s, d=%.3f: Max GSE=%.3f, lower GSE=%.3f",gs,d,gd.GSE,glideslopeError)) -elseif gd.GSE<-0.3 and glideslopeError>0.4 then -gd.FlyThrough="/" -self:E(self.lid..string.format("Got Fly through UP at step %s, d=%.3f: Min GSE=%.3f, lower GSE=%.3f",gs,d,gd.GSE,glideslopeError)) -end -if math.abs(glideslopeError)>math.abs(gd.GSE)then -self:T(self.lid..string.format("Got bigger GSE at step %s, d=%.3f: GSE |%.3f|>|%.3f|",gs,d,glideslopeError,gd.GSE)) -gd.GSE=glideslopeError -end -local aircraftaoa=self:_GetAircraftAoA(playerData) -local aoaopt=aircraftaoa.OnSpeed -if math.abs(AoA-aoaopt)>math.abs(gd.AoA-aoaopt)then -self:T(self.lid..string.format("Got bigger AoA error at step %s, d=%.3f: AoA %.3f>%.3f.",gs,d,AoA,gd.AoA)) -gd.AoA=AoA -end -end -local deltaT=timer.getTime()-playerData.Tlso -local _advice=true -if playerData.TIG0==nil and playerData.difficulty~=AIRBOSS.Difficulty.EASY then -_advice=false -end -if deltaT>=self.LSOdT and _advice then -self:_LSOadvice(playerData,glideslopeError,lineupError) -end -end -if X>self.carrierparam.totlength+self.carrierparam.sterndist then -if playerData.waveoff then -if playerData.landed then -self:_AddToDebrief(playerData,"You were waved off but landed anyway. Airboss wants to talk to you!") -else -self:_AddToDebrief(playerData,"You were waved off.") -end -elseif playerData.boltered then -self:_AddToDebrief(playerData,"You boltered.") -else -self:T("Player was not waved off but flew past the carrier without landing ==> Own wave off!") -self:_AddToDebrief(playerData,"Own waveoff.") -playerData.owo=true -end -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.DEBRIEF) -end -end -function AIRBOSS:_CheckWaveOff(glideslopeError,lineupError,AoA,playerData) -local waveoff=false -local glMax=1.8 -local glMin=-1.2 -local luAbs=3.0 -if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then -glMax=4.0 -glMin=-3.0 -luAbs=5.0 -return false -end -if glideslopeError>glMax then -local text=string.format("\n- Waveoff due to glideslope error %.2f > %.1f degrees!",glideslopeError,glMax) -self:T(self.lid..string.format("%s: %s",playerData.name,text)) -self:_AddToDebrief(playerData,text) -waveoff=true -elseif glideslopeErrorluAbs then -local text=string.format("\n- Waveoff due to line up error |%.1f| > %.1f degrees!",lineupError,luAbs) -self:T(self.lid..string.format("%s: %s",playerData.name,text)) -self:_AddToDebrief(playerData,text) -waveoff=true -end -if playerData.difficulty==AIRBOSS.Difficulty.HARD then -local aoaac=self:_GetAircraftAoA(playerData) -if AoAaoaac.SLOW then -local text=string.format("\n- Waveoff due to AoA %.1f > %.1f!",AoA,aoaac.SLOW) -self:T(self.lid..string.format("%s: %s",playerData.name,text)) -self:_AddToDebrief(playerData,text) -waveoff=true -end -end -return waveoff -end -function AIRBOSS:_CheckFoulDeck(playerData) -local check=false -if playerData.step==AIRBOSS.PatternStep.GROOVE_IM or -playerData.step==AIRBOSS.PatternStep.GROOVE_IC then -check=true -end -if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then -if playerData.step==AIRBOSS.PatternStep.GROOVE_AR or -playerData.step==AIRBOSS.PatternStep.GROOVE_AL then -check=true -end -end -if playerData.wofd==true or check==false then -return -end -local runway=self:_GetZoneRunwayBox() -if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then -runway=self:_GetZoneLandingSpot() -end -local R=250 -self:T(self.lid..string.format("Foul deck check: Scanning Carrier Runway Area. Radius=%.1f m.",R)) -local _,_,_,unitscan=self:GetCoordinate():ScanObjects(R,true,false,false) -local fouldeck=false -local foulunit=nil -for _,_unit in pairs(unitscan)do -local unit=_unit -local inzone=unit:IsInZone(runway) -local isaircraft=unit:IsAir() -local isairborn=unit:InAir() -if inzone and isaircraft and not isairborn then -local text=string.format("Unit %s on landing runway ==> Foul deck!",unit:GetName()) -self:T(self.lid..text) -MESSAGE:New(text,10):ToAllIf(self.Debug) -if self.Debug then -runway:FlareZone(FLARECOLOR.Red,30) -end -fouldeck=true -foulunit=unit -end -end -if playerData and fouldeck then -local text=string.format("Foul deck waveoff due to aircraft %s!",foulunit:GetName()) -self:T(self.lid..string.format("%s: %s",playerData.name,text)) -self:_AddToDebrief(playerData,text) -self:RadioTransmission(self.LSORadio,self.LSOCall.FOULDECK,false,1) -self:RadioTransmission(self.LSORadio,self.LSOCall.WAVEOFF,false,1.2,nil,true) -if playerData.showhints then -local text=string.format("overfly landing area and enter bolter pattern.") -self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,3) -end -playerData.wofd=true -playerData.step=AIRBOSS.PatternStep.DEBRIEF -playerData.warning=nil -playerData.valid=false -if foulunit then -local foulflight=self:_GetFlightFromGroupInQueue(foulunit:GetGroup(),self.flights) -if foulflight and not foulflight.ai then -self:MessageToPlayer(foulflight,"move your ass from my runway. NOW!","AIRBOSS") -end -end -end -return fouldeck -end -function AIRBOSS:_GetSternCoord() -local hdg=self.carrier:GetHeading() -local FB=self:GetFinalBearing() -self.sterncoord:UpdateFromCoordinate(self:GetCoordinate()) -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(8,FB-90,true,true) -elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then -self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(7,FB+90,true,true) -else -self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(9.5,FB+90,true,true) -end -self.sterncoord:SetAltitude(self.carrierparam.deckheight) -return self.sterncoord -end -function AIRBOSS:_GetWire(Lcoord,dc) -local FB=self:GetFinalBearing() -local Scoord=self:_GetSternCoord() -local Ldist=Lcoord:Get2DDistance(Scoord) -dc=dc or 65 -local d=Ldist-dc -if self.mpWireCorrection then -d=d-self.mpWireCorrection -end -local w1=self.carrierparam.wire1 -local w2=self.carrierparam.wire2 -local w3=self.carrierparam.wire3 -local w4=self.carrierparam.wire4 -local wire -if d wire=%d (dc=%.1f)",Ldist,Ldist-dc,wire,dc)) -return wire -end -function AIRBOSS:_Trapped(playerData) -if playerData.unit:InAir()==false then -local unit=playerData.unit -local coord=unit:GetCoordinate() -local v=unit:GetVelocityKMH()-self.carrier:GetVelocityKMH() -local stern=self:_GetSternCoord() -local s=stern:Get2DDistance(coord) -local dcorr=100 -if playerData.actype==AIRBOSS.AircraftCarrier.HORNET then -dcorr=100 -elseif playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then -dcorr=100 -elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then -dcorr=56 -elseif playerData.actype==AIRBOSS.AircraftCarrier.T45C then -dcorr=56 -end -local wire=self:_GetWire(coord,dcorr) -local text=string.format("Player %s _Trapped: v=%.1f km/h, s-dcorr=%.1f m ==> wire=%d (dcorr=%d)",playerData.name,v,s-dcorr,wire,dcorr) -self:T(self.lid..text) -if v>5 then -if wire>4 and v>10 and not playerData.warning then -self:RadioTransmission(self.LSORadio,self.LSOCall.BOLTER,nil,nil,nil,true) -playerData.warning=true -end -self:ScheduleOnce(0.1,self._Trapped,self,playerData) -return -end -if self.Debug then -coord:SmokeBlue() -coord:MarkToAll(text) -stern:MarkToAll("Stern") -end -playerData.wire=wire -local text=string.format("Trapped %d-wire.",wire) -if wire==3 then -text=text.." Well done!" -elseif wire==2 then -text=text.." Not bad, maybe you even get the 3rd next time." -elseif wire==4 then -text=text.." That was scary. You can do better than this!" -elseif wire==1 then -text=text.." Try harder next time!" -end -self:MessageToPlayer(playerData,text,"LSO","") -local hint=string.format("Trapped %d-wire.",wire) -self:_AddToDebrief(playerData,hint,"Groove: IW") -else -local text=string.format("Player %s boltered in trapped function.",playerData.name) -self:T(self.lid..text) -MESSAGE:New(text,5,"DEBUG"):ToAllIf(self.debug) -playerData.boltered=true -end -playerData.step=AIRBOSS.PatternStep.DEBRIEF -playerData.warning=nil -end -function AIRBOSS:_GetZoneInitial(case) -self.zoneInitial=self.zoneInitial or ZONE_POLYGON_BASE:New("Zone CASE I/II Initial") -local radial=self:GetRadial(2,false,false) -local cv=self:GetCoordinate() -local vec2={} -if case==1 then -local c1=cv:Translate(UTILS.NMToMeters(0.5),radial-90) -local c2=cv:Translate(UTILS.NMToMeters(1.3),radial-90):Translate(UTILS.NMToMeters(3),radial) -local c3=cv:Translate(UTILS.NMToMeters(0.4),radial+90):Translate(UTILS.NMToMeters(3),radial) -local c4=cv:Translate(UTILS.NMToMeters(1.0),radial) -local c5=cv -vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2()} -else -local c1=cv:Translate(UTILS.NMToMeters(0.5),radial-90) -local c2=c1:Translate(UTILS.NMToMeters(0.5),radial) -local c3=cv:Translate(UTILS.NMToMeters(1.2),radial-90):Translate(UTILS.NMToMeters(3),radial) -local c4=cv:Translate(UTILS.NMToMeters(1.2),radial+90):Translate(UTILS.NMToMeters(3),radial) -local c5=cv:Translate(UTILS.NMToMeters(0.5),radial) -local c6=cv -vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2(),c6:GetVec2()} -end -self.zoneInitial:UpdateFromVec2(vec2) -return self.zoneInitial -end -function AIRBOSS:_GetZoneLineup() -self.zoneLineup=self.zoneLineup or ZONE_POLYGON_BASE:New("Zone Lineup") -local fbi=self:GetRadial(1,false,false) -local st=self:_GetOptLandingCoordinate() -local c1=st -local c2=st:Translate(UTILS.NMToMeters(0.50),fbi+15) -local c3=st:Translate(UTILS.NMToMeters(0.50),fbi+self.lue._max-0.05) -local c4=st:Translate(UTILS.NMToMeters(0.77),fbi+self.lue._max-0.05) -local c5=c4:Translate(UTILS.NMToMeters(0.25),fbi-90) -local vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2()} -self.zoneLineup:UpdateFromVec2(vec2) -return self.zoneLineup -end -function AIRBOSS:_GetZoneGroove(l,w,b) -self.zoneGroove=self.zoneGroove or ZONE_POLYGON_BASE:New("Zone Groove") -l=l or 1.50 -w=w or 0.25 -b=b or 0.10 -local fbi=self:GetRadial(1,false,false) -local st=self:_GetSternCoord() -local c1=st:Translate(self.carrierparam.totwidthstarboard,fbi-90) -local c2=st:Translate(UTILS.NMToMeters(0.10),fbi-90):Translate(UTILS.NMToMeters(0.3),fbi) -local c3=st:Translate(UTILS.NMToMeters(0.25),fbi-90):Translate(UTILS.NMToMeters(l),fbi) -local c4=st:Translate(UTILS.NMToMeters(w/2),fbi+90):Translate(UTILS.NMToMeters(l),fbi) -local c5=st:Translate(UTILS.NMToMeters(b),fbi+90):Translate(UTILS.NMToMeters(0.3),fbi) -local c6=st:Translate(self.carrierparam.totwidthport,fbi+90) -local vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2(),c6:GetVec2()} -self.zoneGroove:UpdateFromVec2(vec2) -return self.zoneGroove -end -function AIRBOSS:_GetZoneBullseye(case) -local radius=UTILS.NMToMeters(1) -local distance=UTILS.NMToMeters(3) -local radial=self:GetRadial(case,false,false) -local coord=self:GetCoordinate():Translate(distance,radial) -local vec2=coord:GetVec2() -local zone=ZONE_RADIUS:New("Zone Bullseye",vec2,radius) -return zone -end -function AIRBOSS:_GetZoneDirtyUp(case) -local radius=UTILS.NMToMeters(1) -local distance=UTILS.NMToMeters(9) -local radial=self:GetRadial(case,false,false) -local coord=self:GetCoordinate():Translate(distance,radial) -local vec2=coord:GetVec2() -local zone=ZONE_RADIUS:New("Zone Dirty Up",vec2,radius) -return zone -end -function AIRBOSS:_GetZoneArcOut(case) -local radius=UTILS.NMToMeters(1.25) -local distance=UTILS.NMToMeters(11.75) -local radial=self:GetRadial(case,false,false) -local coord=self:GetCoordinate():Translate(distance,radial) -local zone=ZONE_RADIUS:New("Zone Arc Out",coord:GetVec2(),radius) -return zone -end -function AIRBOSS:_GetZoneArcIn(case) -local radius=UTILS.NMToMeters(1.25) -local radial=self:GetRadial(case,false,true) -local alpha=math.rad(self.holdingoffset) -local x=14 -local distance=UTILS.NMToMeters(x) -local coord=self:GetCoordinate():Translate(distance,radial) -local zone=ZONE_RADIUS:New("Zone Arc In",coord:GetVec2(),radius) -return zone -end -function AIRBOSS:_GetZonePlatform(case) -local radius=UTILS.NMToMeters(1) -local radial=self:GetRadial(case,false,true) -local alpha=math.rad(self.holdingoffset) -local distance=UTILS.NMToMeters(19) -local coord=self:GetCoordinate():Translate(distance,radial) -local zone=ZONE_RADIUS:New("Zone Platform",coord:GetVec2(),radius) -return zone -end -function AIRBOSS:_GetZoneCorridor(case,l) -l=l or 31 -local radial=self:GetRadial(case,false,false) -local offset=self:GetRadial(case,false,true) -local dx=5 -local w=2 -local w2=w/2 -local d=12 -local cv=self:GetCoordinate() -local c={} -c[1]=cv:Translate(-UTILS.NMToMeters(dx),radial) -if math.abs(self.holdingoffset)>=5 then -c[2]=c[1]:Translate(UTILS.NMToMeters(w2),radial-90) -c[3]=c[2]:Translate(UTILS.NMToMeters(d+dx+w2),radial) -c[4]=cv:Translate(UTILS.NMToMeters(15),offset):Translate(UTILS.NMToMeters(1),offset-90) -c[5]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset-90) -c[6]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset+90) -c[7]=cv:Translate(UTILS.NMToMeters(13),offset):Translate(UTILS.NMToMeters(1),offset+90) -c[8]=cv:Translate(UTILS.NMToMeters(11),radial):Translate(UTILS.NMToMeters(1),radial+90) -c[9]=c[1]:Translate(UTILS.NMToMeters(w2),radial+90) -else -c[2]=c[1]:Translate(UTILS.NMToMeters(w2),radial-90) -c[3]=c[2]:Translate(UTILS.NMToMeters(dx+l),radial) -c[4]=c[3]:Translate(UTILS.NMToMeters(w),radial+90) -c[5]=c[1]:Translate(UTILS.NMToMeters(w2),radial+90) -end -local p={} -for _i,_c in ipairs(c)do -if self.Debug then -end -p[_i]=_c:GetVec2() -end -local zone=ZONE_POLYGON_BASE:New("CASE II/III Approach Corridor",p) -return zone -end -function AIRBOSS:_GetZoneCarrierBox() -self.zoneCarrierbox=self.zoneCarrierbox or ZONE_POLYGON_BASE:New("Carrier Box Zone") -local S=self:_GetSternCoord() -local hdg=self:GetHeading(false) -local p={} -p[1]=S:Translate(self.carrierparam.totwidthstarboard,hdg+90) -p[2]=p[1]:Translate(self.carrierparam.totlength,hdg) -p[3]=p[2]:Translate(self.carrierparam.totwidthstarboard+self.carrierparam.totwidthport,hdg-90) -p[4]=p[3]:Translate(self.carrierparam.totlength,hdg-180) -local vec2={} -for _,coord in ipairs(p)do -table.insert(vec2,coord:GetVec2()) -end -self.zoneCarrierbox:UpdateFromVec2(vec2) -return self.zoneCarrierbox -end -function AIRBOSS:_GetZoneRunwayBox() -self.zoneRunwaybox=self.zoneRunwaybox or ZONE_POLYGON_BASE:New("Landing Runway Zone") -local S=self:_GetSternCoord() -local FB=self:GetFinalBearing(false) -local p={} -p[1]=S:Translate(self.carrierparam.rwywidth*0.5,FB+90) -p[2]=p[1]:Translate(self.carrierparam.rwylength,FB) -p[3]=p[2]:Translate(self.carrierparam.rwywidth,FB-90) -p[4]=p[3]:Translate(self.carrierparam.rwylength,FB-180) -local vec2={} -for _,coord in ipairs(p)do -table.insert(vec2,coord:GetVec2()) -end -self.zoneRunwaybox:UpdateFromVec2(vec2) -return self.zoneRunwaybox -end -function AIRBOSS:_GetZoneAbeamLandingSpot() -local S=self:_GetOptLandingCoordinate() -local FB=self:GetFinalBearing(false) -local p={} -p[1]=S:Translate(15,FB):Translate(15,FB+90) -p[2]=S:Translate(-15,FB):Translate(15,FB+90) -p[3]=S:Translate(-15,FB):Translate(15,FB-90) -p[4]=S:Translate(15,FB):Translate(15,FB-90) -local vec2={} -for _,coord in ipairs(p)do -table.insert(vec2,coord:GetVec2()) -end -local zone=ZONE_POLYGON_BASE:New("Abeam Landing Spot Zone",vec2) -return zone -end -function AIRBOSS:_GetZoneLandingSpot() -local S=self:_GetLandingSpotCoordinate() -local FB=self:GetFinalBearing(false) -local p={} -p[1]=S:Translate(10,FB):Translate(10,FB+90) -p[2]=S:Translate(-10,FB):Translate(10,FB+90) -p[3]=S:Translate(-10,FB):Translate(10,FB-90) -p[4]=S:Translate(10,FB):Translate(10,FB-90) -local vec2={} -for _,coord in ipairs(p)do -table.insert(vec2,coord:GetVec2()) -end -local zone=ZONE_POLYGON_BASE:New("Landing Spot Zone",vec2) -return zone -end -function AIRBOSS:_GetZoneHolding(case,stack) -local zoneHolding=nil -if stack<=0 then -self:E(self.lid.."ERROR: Stack <= 0 in _GetZoneHolding!") -self:E({case=case,stack=stack}) -return nil -end -local patternalt,c1,c2=self:_GetMarshalAltitude(stack,case) -if case==1 then -local hdg=self:GetHeading() -local D=UTILS.NMToMeters(2.5) -local Post=self:GetCoordinate():Translate(D,hdg+270) -self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone",Post:GetVec2(),self.marshalradius) -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone",self.carrier:GetVec2(),UTILS.NMToMeters(5)) -end -else -local radial=self:GetRadial(case,false,true) -local p={} -p[1]=c2:Translate(UTILS.NMToMeters(1),radial-90):GetVec2() -p[2]=c1:Translate(UTILS.NMToMeters(1),radial-90):GetVec2() -p[3]=c1:Translate(UTILS.NMToMeters(7),radial+90):GetVec2() -p[4]=c2:Translate(UTILS.NMToMeters(7),radial+90):GetVec2() -self.zoneHolding=self.zoneHolding or ZONE_POLYGON_BASE:New("CASE II/III Holding Zone") -self.zoneHolding:UpdateFromVec2(p) -end -return self.zoneHolding -end -function AIRBOSS:_GetZoneCommence(case,stack) -local zone -if case==1 then -local hdg=self:GetHeading() -local D=UTILS.NMToMeters(4.75) -local R=UTILS.NMToMeters(1) -local Three=self:GetCoordinate():Translate(D,hdg+275) -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -local Dx=UTILS.NMToMeters(2.25) -local Dz=UTILS.NMToMeters(2.25) -R=UTILS.NMToMeters(1) -Three=self:GetCoordinate():Translate(Dz,hdg-90):Translate(Dx,hdg-180) -end -self.zoneCommence=self.zoneCommence or ZONE_RADIUS:New("CASE I Commence Zone") -self.zoneCommence:UpdateFromVec2(Three:GetVec2(),R) -else -stack=stack or 1 -local l=20+stack -local offset=self:GetRadial(case,false,true) -local cv=self:GetCoordinate() -local c={} -c[1]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset-90) -c[2]=cv:Translate(UTILS.NMToMeters(l+2.5),offset):Translate(UTILS.NMToMeters(1),offset-90) -c[3]=cv:Translate(UTILS.NMToMeters(l+2.5),offset):Translate(UTILS.NMToMeters(1),offset+90) -c[4]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset+90) -local p={} -for _i,_c in ipairs(c)do -p[_i]=_c:GetVec2() -end -self.zoneCommence=self.zoneCommence or ZONE_POLYGON_BASE:New("CASE II/III Commence Zone") -self.zoneCommence:UpdateFromVec2(p) -end -return self.zoneCommence -end -function AIRBOSS:_AttitudeMonitor(playerData) -local unit=playerData.unit -local aoa=unit:GetAoA() -local yaw=unit:GetYaw() -local roll=unit:GetRoll() -local pitch=unit:GetPitch() -local dist=playerData.unit:GetCoordinate():Get2DDistance(self:GetCoordinate()) -local dx,dz,rho,phi=self:_GetDistances(unit) -local wind=unit:GetCoordinate():GetWindWithTurbulenceVec3() -local velo=unit:GetVelocityVec3() -local vabs=UTILS.VecNorm(velo) -local rwy=false -local step=playerData.step -if playerData.step==AIRBOSS.PatternStep.FINAL or -playerData.step==AIRBOSS.PatternStep.GROOVE_XX or -playerData.step==AIRBOSS.PatternStep.GROOVE_IM or -playerData.step==AIRBOSS.PatternStep.GROOVE_IC or -playerData.step==AIRBOSS.PatternStep.GROOVE_AR or -playerData.step==AIRBOSS.PatternStep.GROOVE_AL or -playerData.step==AIRBOSS.PatternStep.GROOVE_LC or -playerData.step==AIRBOSS.PatternStep.GROOVE_IW then -step=self:_GS(step,-1) -rwy=true -end -local relhead=self:_GetRelativeHeading(playerData.unit,rwy) -local text=string.format("Pattern step: %s",step) -text=text..string.format("\nAoA=%.1f° = %.1f Units | |V|=%.1f knots",aoa,self:_AoADeg2Units(playerData,aoa),UTILS.MpsToKnots(vabs)) -if self.Debug then -text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s",velo.x,velo.y,velo.z) -text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s",wind.x,wind.y,wind.z) -end -text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°",pitch,roll,yaw) -text=text..string.format("\nClimb Angle=%.1f° | Rate=%d ft/min",unit:GetClimbAngle(),velo.y*196.85) -local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit) -local vplayer=playerData.unit:GetVelocityKMH() -local vcarrier=self.carrier:GetVelocityKMH() -local dv=math.abs(vplayer-vcarrier) -local alt=self:_GetAltCarrier(playerData.unit) -text=text..string.format("\nDist=%.1f m Alt=%.1f m delta|V|=%.1f km/h",dist,alt,dv) -if playerData.step==AIRBOSS.PatternStep.FINAL or -playerData.step==AIRBOSS.PatternStep.GROOVE_XX or -playerData.step==AIRBOSS.PatternStep.GROOVE_IM or -playerData.step==AIRBOSS.PatternStep.GROOVE_IC or -playerData.step==AIRBOSS.PatternStep.GROOVE_AR or -playerData.step==AIRBOSS.PatternStep.GROOVE_AL or -playerData.step==AIRBOSS.PatternStep.GROOVE_LC or -playerData.step==AIRBOSS.PatternStep.GROOVE_IW then -local lue=self:_Lineup(playerData.unit,true) -local gle=self:_Glideslope(playerData.unit) -text=text..string.format("\nGamma=%.1f° | Rho=%.1f°",relhead,phi) -text=text..string.format("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units",lue,gle,self:_AoADeg2Units(playerData,aoa)) -local grade,points,analysis=self:_LSOgrade(playerData) -text=text..string.format("\nTgroove=%.1f sec",self:_GetTimeInGroove(playerData)) -text=text..string.format("\nGrade: %s %.1f PT - %s",grade,points,analysis) -else -text=text..string.format("\nR=%.2f NM | X=%d Z=%d m",UTILS.MetersToNM(rho),dx,dz) -text=text..string.format("\nGamma=%.1f° | Rho=%.1f°",relhead,phi) -end -MESSAGE:New(text,1,nil,true):ToClient(playerData.client) -end -function AIRBOSS:_Glideslope(unit,optangle) -if optangle==nil then -if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then -optangle=3.0 -else -optangle=3.5 -end -end -local landingcoord=self:_GetOptLandingCoordinate() -local x=unit:GetCoordinate():Get2DDistance(landingcoord) -local h=self:_GetAltCarrier(unit) -if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then -h=unit:GetAltitude()-(UTILS.FeetToMeters(50)+self.carrierparam.deckheight+2) -end -local glideslope=math.atan(h/x) -local gs=math.deg(glideslope)-optangle -return gs -end -function AIRBOSS:_Glideslope2(unit,optangle) -if optangle==nil then -if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then -optangle=3.0 -else -optangle=3.5 -end -end -local landingcoord=self:_GetOptLandingCoordinate() -local x=unit:GetCoordinate():Get3DDistance(landingcoord) -local h=self:_GetAltCarrier(unit) -if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then -h=unit:GetAltitude()-(UTILS.FeetToMeters(50)+self.carrierparam.deckheight+2) -end -local glideslope=math.asin(h/x) -local gs=math.deg(glideslope)-optangle -self:T3(self.lid..string.format("Glide slope error = %.1f, x=%.1f h=%.1f",gs,x,h)) -return gs -end -function AIRBOSS:_Lineup(unit,runway) -local landingcoord=self:_GetOptLandingCoordinate() -local A=landingcoord:GetVec3() -local B=unit:GetVec3() -local C=UTILS.VecSubstract(A,B) -C.y=0.0 -local X=self.carrier:GetOrientationX() -X.y=0.0 -if runway then -X=UTILS.Rotate2D(X,-self.carrierparam.rwyangle) -end -local x=UTILS.VecDot(X,C) -local Z=self.carrier:GetOrientationZ() -Z.y=0.0 -if runway then -Z=UTILS.Rotate2D(Z,-self.carrierparam.rwyangle) -end -local z=UTILS.VecDot(Z,C) -local lineup=math.deg(math.atan2(z,x)) -return lineup -end -function AIRBOSS:_GetAltCarrier(unit) -local h=unit:GetAltitude()-self.carrierparam.deckheight-2 -return h -end -function AIRBOSS:_GetOptLandingCoordinate() -self.landingcoord:UpdateFromCoordinate(self:_GetSternCoord()) -local FB=self:GetFinalBearing(false) -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35,FB-90,true,true) -self.landingcoord:SetAltitude(UTILS.FeetToMeters(120)) -else -if self.carrierparam.wire3 then -local w3=self.carrierparam.wire3 -self.landingcoord:Translate(w3,FB,true,true) -end -self.landingcoord.y=self.landingcoord.y+2 -end -return self.landingcoord -end -function AIRBOSS:_GetLandingSpotCoordinate() -self.landingspotcoord:UpdateFromCoordinate(self:_GetSternCoord()) -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -local hdg=self:GetHeading() -self.landingspotcoord:Translate(57,hdg,true,true):SetAltitude(self.carrierparam.deckheight) -end -return self.landingspotcoord -end -function AIRBOSS:GetHeading(magnetic) -self:F3({magnetic=magnetic}) -local hdg=self.carrier:GetHeading() -if magnetic then -hdg=hdg-self.magvar -end -if hdg<0 then -hdg=hdg+360 -end -return hdg -end -function AIRBOSS:GetBRC() -return self:GetHeading(true) -end -function AIRBOSS:GetWind(alt,magnetic,coord) -local cv=coord or self:GetCoordinate() -local Wdir,Wspeed=cv:GetWind(alt or 50) -if magnetic then -Wdir=Wdir-self.magvar -if Wdir<0 then -Wdir=Wdir+360 -end -end -return Wdir,Wspeed -end -function AIRBOSS:GetWindOnDeck(alt) -local cv=self:GetCoordinate() -local vc=self.carrier:GetVelocityVec3() -local xc=self.carrier:GetOrientationX() -local zc=self.carrier:GetOrientationZ() -xc=UTILS.Rotate2D(xc,-self.carrierparam.rwyangle) -zc=UTILS.Rotate2D(zc,-self.carrierparam.rwyangle) -local vw=cv:GetWindWithTurbulenceVec3(alt or 50) -local vT=UTILS.VecSubstract(vw,vc) -local vpa=UTILS.VecDot(vT,xc) -local vpp=UTILS.VecDot(vT,zc) -local vabs=UTILS.VecNorm(vT) -return-vpa,vpp,vabs -end -function AIRBOSS:GetHeadingIntoWind(magnetic,coord) -local windfrom,vwind=self:GetWind(nil,nil,coord) -local intowind=windfrom-self.carrierparam.rwyangle -if vwind<0.1 then -intowind=self:GetHeading() -end -if magnetic then -intowind=intowind-self.magvar -end -if intowind<0 then -intowind=intowind+360 -end -return intowind -end -function AIRBOSS:GetBRCintoWind() -return self:GetHeadingIntoWind(true) -end -function AIRBOSS:GetFinalBearing(magnetic) -local fb=self:GetHeading(magnetic) -fb=fb+self.carrierparam.rwyangle -if fb<0 then -fb=fb+360 -end -return fb -end -function AIRBOSS:GetRadial(case,magnetic,offset,inverse) -case=case or self.case -local radial -if case==1 then -radial=self:GetFinalBearing(magnetic)-180 -elseif case==2 then -radial=self:GetHeading(magnetic)-180 -if offset then -radial=radial+self.holdingoffset -end -elseif case==3 then -radial=self:GetFinalBearing(magnetic)-180 -if offset then -radial=radial+self.holdingoffset -end -end -if radial<0 then -radial=radial+360 -end -if inverse then -radial=radial-180 -if radial<0 then -radial=radial+360 -end -end -return radial -end -function AIRBOSS:_GetDeltaHeading(hdg1,hdg2) -local V={} -V.x=math.cos(math.rad(hdg1)) -V.y=0 -V.z=math.sin(math.rad(hdg1)) -local W={} -W.x=math.cos(math.rad(hdg2)) -W.y=0 -W.z=math.sin(math.rad(hdg2)) -local alpha=UTILS.VecAngle(V,W) -return alpha -end -function AIRBOSS:_GetRelativeHeading(unit,runway) -local vC=self.carrier:GetOrientationX() -if runway then -vC=UTILS.Rotate2D(vC,-self.carrierparam.rwyangle) -end -local vP=unit:GetOrientationX() -vC.y=0;vP.y=0 -local rhdg=UTILS.VecAngle(vC,vP) -return rhdg -end -function AIRBOSS:_GetRelativeVelocity(unit) -local vC=self.carrier:GetVelocityVec3() -local vP=unit:GetVelocityVec3() -vC.y=0;vP.y=0 -local v=UTILS.VecSubstract(vP,vC) -return UTILS.VecNorm(v),v -end -function AIRBOSS:_GetDistances(unit) -local a=self.carrier:GetVec3() -local b=unit:GetVec3() -local c={x=b.x-a.x,y=0,z=b.z-a.z} -local x=self.carrier:GetOrientationX() -local dx=UTILS.VecDot(x,c) -local z=self.carrier:GetOrientationZ() -local dz=UTILS.VecDot(z,c) -local rho=math.sqrt(dx*dx+dz*dz) -local phi=math.deg(math.atan2(dz,dx)) -if phi<0 then -phi=phi+360 -end -return dx,dz,rho,phi -end -function AIRBOSS:_CheckLimits(X,Z,check) -local nextXmin=check.LimitXmin==nil or(check.LimitXmin and(check.LimitXmin<0 and X<=check.LimitXmin or check.LimitXmin>=0 and X>=check.LimitXmin)) -local nextXmax=check.LimitXmax==nil or(check.LimitXmax and(check.LimitXmax<0 and X>=check.LimitXmax or check.LimitXmax>=0 and X<=check.LimitXmax)) -local nextZmin=check.LimitZmin==nil or(check.LimitZmin and(check.LimitZmin<0 and Z<=check.LimitZmin or check.LimitZmin>=0 and Z>=check.LimitZmin)) -local nextZmax=check.LimitZmax==nil or(check.LimitZmax and(check.LimitZmax<0 and Z>=check.LimitZmax or check.LimitZmax>=0 and Z<=check.LimitZmax)) -local next=nextXmin and nextXmax and nextZmin and nextZmax -local text=string.format("step=%s: next=%s: X=%d Xmin=%s Xmax=%s | Z=%d Zmin=%s Zmax=%s", -check.name,tostring(next),X,tostring(check.LimitXmin),tostring(check.LimitXmax),Z,tostring(check.LimitZmin),tostring(check.LimitZmax)) -self:T3(self.lid..text) -return next -end -function AIRBOSS:_LSOadvice(playerData,glideslopeError,lineupError) -local advice=0 -if glideslopeError>self.gle.HIGH then -self:RadioTransmission(self.LSORadio,self.LSOCall.HIGH,true,nil,nil,true) -advice=advice+self.LSOCall.HIGH.duration -elseif glideslopeError>self.gle.High then -self:RadioTransmission(self.LSORadio,self.LSOCall.HIGH,false,nil,nil,true) -advice=advice+self.LSOCall.HIGH.duration -elseif glideslopeErrorself.lue.RIGHT then -self:RadioTransmission(self.LSORadio,self.LSOCall.RIGHTFORLINEUP,true,nil,nil,true) -advice=advice+self.LSOCall.RIGHTFORLINEUP.duration -elseif lineupError>self.lue.Right then -self:RadioTransmission(self.LSORadio,self.LSOCall.RIGHTFORLINEUP,false,nil,nil,true) -advice=advice+self.LSOCall.RIGHTFORLINEUP.duration -else -end -local AOA=playerData.unit:GetAoA() -local acaoa=self:_GetAircraftAoA(playerData) -if playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then -if AOA>acaoa.SLOW then -self:RadioTransmission(self.LSORadio,self.LSOCall.SLOW,true,nil,nil,true) -advice=advice+self.LSOCall.SLOW.duration -elseif AOA>acaoa.Slow then -self:RadioTransmission(self.LSORadio,self.LSOCall.SLOW,false,nil,nil,true) -advice=advice+self.LSOCall.SLOW.duration -elseif AOA>acaoa.OnSpeedMax then -elseif AOA=16.4 and t<=16.6 then -grade="_OK_" -end -return grade -end -function AIRBOSS:_LSOgrade(playerData) -local function count(base,pattern) -return select(2,string.gsub(base,pattern,"")) -end -local GXX,nXX=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.XX) -local GIM,nIM=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.IM) -local GIC,nIC=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.IC) -local GAR,nAR=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.AR) -local G=GXX.." "..GIM.." ".." "..GIC.." "..GAR -local N=nXX+nIM+nIC+nAR -local nL=count(G,'_')/2 -local nS=count(G,'%(') -local nN=N-nS-nL -local Tgroove=playerData.Tgroove -local TgrooveUnicorn=Tgroove and(Tgroove>=15.0 and Tgroove<=18.99)or false -local grade -local points -if N==0 and TgrooveUnicorn then -grade="_OK_" -points=5.0 -G="Unicorn" -else -if nL>0 then -grade="--" -points=2.0 -elseif nN>0 then -grade="(OK)" -points=3.0 -else -grade="OK" -points=4.0 -end -end -G=G:gsub("%)%(","") -G=G:gsub("__","") -local text="LSO grade:\n" -text=text..G.."\n" -text=text.."Grade = "..grade.." points = "..points.."\n" -text=text.."# of total deviations = "..N.."\n" -text=text.."# of large deviations _ = "..nL.."\n" -text=text.."# of normal deviations = "..nN.."\n" -text=text.."# of small deviations ( = "..nS.."\n" -self:T2(self.lid..text) -if playerData.wop then -if playerData.lig then -grade="WO" -points=1.0 -G="LIG" -else -grade="WOP" -points=2.0 -G="n/a" -end -elseif playerData.wofd then -if playerData.landed then -grade="CUT" -points=0.0 -else -grade="WOFD" -points=-1.0 -end -G="n/a" -elseif playerData.owo then -grade="OWO" -points=2.0 -if N==0 then -G="n/a" -end -elseif playerData.waveoff then -if playerData.landed then -grade="CUT" -points=0.0 -else -grade="WO" -points=1.0 -end -elseif playerData.boltered then -grade="-- (BOLTER)" -points=2.5 -end -return grade,points,G -end -function AIRBOSS:_Flightdata2Text(playerData,groovestep) -local function little(text) -return string.format("(%s)",text) -end -local function underline(text) -return string.format("_%s_",text) -end -local fdata=playerData.groove[groovestep] -if fdata==nil then -self:T3(self.lid.."Flight data is nil.") -return"",0 -end -local step=fdata.Step -local AOA=fdata.AoA -local GSE=fdata.GSE -local LUE=fdata.LUE -local ROL=fdata.Roll -local acaoa=self:_GetAircraftAoA(playerData) -local P=nil -if step==AIRBOSS.PatternStep.GROOVE_XX and ROL<=4.0 and playerData.case<3 then -if LUE>self.lue.RIGHT then -P=underline("AA") -elseif -LUE>self.lue.RightMed then -P="AA " -elseif -LUE>self.lue.Right then -P=little("AA") -end -end -local O=nil -if step==AIRBOSS.PatternStep.GROOVE_XX then -if LUEacaoa.SLOW then -S=underline("SLO") -elseif AOA>acaoa.Slow then -S="SLO" -elseif AOA>acaoa.OnSpeedMax then -S=little("SLO") -elseif AOAself.gle.HIGH then -A=underline("H") -elseif GSE>self.gle.High then -A="H" -elseif GSE>self.gle._max then -A=little("H") -elseif GSEself.lue.RIGHT then -D=underline("LUL") -elseif LUE>self.lue.Right then -D="LUL" -elseif LUE>self.lue._max then -D=little("LUL") -elseif playerData.case<3 then -if LUEpos.Xmax then -self:T(string.format("Xmax: X=%d > %d=Xmax",X,pos.Xmax)) -abort=true -elseif pos.Zmin and Zpos.Zmax then -self:T(string.format("Zmax: Z=%d > %d=Zmax",Z,pos.Zmax)) -abort=true -end -return abort -end -function AIRBOSS:_TooFarOutText(X,Z,posData) -local text="you are too " -local xtext=nil -if posData.Xmin and XposData.Xmax then -if posData.Xmax>=0 then -xtext="far ahead of " -else -xtext="close to " -end -end -local ztext=nil -if posData.Zmin and ZposData.Zmax then -if posData.Zmax>=0 then -ztext="far starboard of " -else -ztext="too close to " -end -end -if xtext and ztext then -text=text..xtext.." and "..ztext -elseif xtext then -text=text..xtext -elseif ztext then -text=text..ztext -end -text=text.."the carrier." -if xtext==nil and ztext==nil then -text="you are too far from where you should be!" -end -return text -end -function AIRBOSS:_AbortPattern(playerData,X,Z,posData,patternwo) -local text=self:_TooFarOutText(X,Z,posData) -local dtext=string.format("Abort: X=%d Xmin=%s, Xmax=%s | Z=%d Zmin=%s Zmax=%s",X,tostring(posData.Xmin),tostring(posData.Xmax),Z,tostring(posData.Zmin),tostring(posData.Zmax)) -self:T(self.lid..dtext) -self:MessageToPlayer(playerData,text,"LSO") -if patternwo then -playerData.wop=true -self:_AddToDebrief(playerData,string.format("Pattern wave off: %s",text)) -self:RadioTransmission(self.LSORadio,self.LSOCall.DEPARTANDREENTER,false,3,nil,nil,true) -playerData.step=AIRBOSS.PatternStep.DEBRIEF -playerData.warning=nil -end -end -function AIRBOSS:_PlayerHint(playerData,delay,soundoff) -if not playerData.showhints then -return -end -local alt,aoa,dist,speed=self:_GetAircraftParameters(playerData) -local hintAlt,debriefAlt,callAlt=self:_AltitudeCheck(playerData,alt) -local hintSpeed,debriefSpeed,callSpeed=self:_SpeedCheck(playerData,speed) -local hintAoA,debriefAoA,callAoA=self:_AoACheck(playerData,aoa) -local hintDist,debriefDist,callDist=self:_DistanceCheck(playerData,dist) -local hint="" -if hintAlt and hintAlt~=""then -hint=hint.."\n"..hintAlt -end -if hintSpeed and hintSpeed~=""then -hint=hint.."\n"..hintSpeed -end -if hintAoA and hintAoA~=""then -hint=hint.."\n"..hintAoA -end -if hintDist and hintDist~=""then -hint=hint.."\n"..hintDist -end -local debrief="" -if debriefAlt and debriefAlt~=""then -debrief=debrief.."\n- "..debriefAlt -end -if debriefSpeed and debriefSpeed~=""then -debrief=debrief.."\n- "..debriefSpeed -end -if debriefAoA and debriefAoA~=""then -debrief=debrief.."\n- "..debriefAoA -end -if debriefDist and debriefDist~=""then -debrief=debrief.."\n- "..debriefDist -end -if debrief~=""then -self:_AddToDebrief(playerData,debrief) -end -delay=delay or 0 -if not soundoff then -if callAlt then -self:Sound2Player(playerData,self.LSORadio,callAlt,false,delay) -delay=delay+callAlt.duration+0.5 -end -if callSpeed then -self:Sound2Player(playerData,self.LSORadio,callSpeed,false,delay) -delay=delay+callSpeed.duration+0.5 -end -if callAoA then -self:Sound2Player(playerData,self.LSORadio,callAoA,false,delay) -delay=delay+callAoA.duration+0.5 -end -if callDist then -self:Sound2Player(playerData,self.LSORadio,callDist,false,delay) -delay=delay+callDist.duration+0.5 -end -end -if playerData.step==AIRBOSS.PatternStep.ARCIN then -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -local radial=self:GetRadial(playerData.case,true,false,true) -local turn="right" -if self.holdingoffset<0 then -turn="left" -end -hint=hint..string.format("\nTurn %s and select TACAN %03d°.",turn,radial) -end -end -if playerData.step==AIRBOSS.PatternStep.DIRTYUP then -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then -hint=hint.."\nFAF! Checks completed. Nozzles 50°." -else -hint=hint.."\nDirty up! Hook, gear and flaps down." -end -end -end -if playerData.step==AIRBOSS.PatternStep.BULLSEYE then -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -if playerData.actype==AIRBOSS.AircraftCarrier.HORNET then -hint=hint..string.format("\nIntercept glideslope and follow the needles.") -else -hint=hint..string.format("\nIntercept glideslope.") -end -end -end -if hint~=""then -local text=string.format("%s%s",playerData.step,hint) -self:MessageToPlayer(playerData,hint,"AIRBOSS","") -end -end -function AIRBOSS:_StepHint(playerData,step) -step=step or playerData.step -if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.showhints then -local alt,aoa,dist,speed=self:_GetAircraftParameters(playerData,step) -local hint="" -if alt then -hint=hint..string.format("\nAltitude %d ft",UTILS.MetersToFeet(alt)) -end -if aoa then -hint=hint..string.format("\nAoA %.1f",self:_AoADeg2Units(playerData,aoa)) -end -if speed then -hint=hint..string.format("\nSpeed %d knots",UTILS.MpsToKnots(speed)) -end -if dist then -hint=hint..string.format("\nDistance to the boat %.1f NM",UTILS.MetersToNM(dist)) -end -if step==AIRBOSS.PatternStep.LATEBREAK then -if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then -hint=hint.."\nWing Sweep 20°, Gear DOWN < 280 KIAS." -end -end -if step==AIRBOSS.PatternStep.ABEAM then -if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then -hint=hint.."\nNozzles 50°-60°. Antiskid OFF. Lights OFF." -elseif playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then -hint=hint.."\nSlats/Flaps EXTENDED < 225 KIAS. DLC SELECTED. Auto Throttle IF DESIRED." -else -hint=hint.."\nDirty up! Gear DOWN, flaps DOWN. Check hook down." -end -end -if hint~=""then -local text=string.format("Optimal setup at next step %s:%s",step,hint) -self:MessageToPlayer(playerData,text,"AIRBOSS","",nil,false,1) -end -end -end -function AIRBOSS:_AltitudeCheck(playerData,altopt) -if altopt==nil then -return nil,nil -end -local altitude=playerData.unit:GetAltitude() -local lowscore,badscore=self:_GetGoodBadScore(playerData) -local _error=(altitude-altopt)/altopt*100 -local radiocall=nil -local hint="" -if _error>badscore then -radiocall=self:_NewRadioCall(self.LSOCall.HIGH,"Paddles","") -elseif _error>lowscore then -radiocall=self:_NewRadioCall(self.LSOCall.HIGH,"Paddles","") -elseif _error<-badscore then -radiocall=self:_NewRadioCall(self.LSOCall.LOW,"Paddles","") -elseif _error<-lowscore then -radiocall=self:_NewRadioCall(self.LSOCall.LOW,"Paddles","") -else -hint=string.format("Good altitude. ") -end -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -hint=hint..string.format("Optimal altitude is %d ft.",UTILS.MetersToFeet(altopt)) -elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then -hint="" -elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then -hint="" -end -local debrief=string.format("Altitude %d ft = %d%% deviation from %d ft.",UTILS.MetersToFeet(altitude),_error,UTILS.MetersToFeet(altopt)) -return hint,debrief,radiocall -end -function AIRBOSS:_AoACheck(playerData,optaoa) -if optaoa==nil then -return nil,nil -end -local lowscore,badscore=self:_GetGoodBadScore(playerData) -local aoa=playerData.unit:GetAoA() -local _error=(aoa-optaoa)/optaoa*100 -local aircraftaoa=self:_GetAircraftAoA(playerData) -local radiocall=nil -local hint="" -if aoa>=aircraftaoa.SLOW then -radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"Paddles","") -elseif aoa>=aircraftaoa.Slow then -radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"Paddles","") -elseif aoa>=aircraftaoa.OnSpeedMax then -hint="Your're a little slow. " -elseif aoa>=aircraftaoa.OnSpeedMin then -hint="You're on speed. " -elseif aoa>=aircraftaoa.Fast then -hint="You're a little fast. " -elseif aoa>=aircraftaoa.FAST then -radiocall=self:_NewRadioCall(self.LSOCall.FAST,"Paddles","") -else -radiocall=self:_NewRadioCall(self.LSOCall.FAST,"Paddles","") -end -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -hint=hint..string.format("Optimal AoA is %.1f.",self:_AoADeg2Units(playerData,optaoa)) -elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then -hint="" -elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then -hint="" -end -local debrief=string.format("AoA %.1f = %d%% deviation from %.1f.",self:_AoADeg2Units(playerData,aoa),_error,self:_AoADeg2Units(playerData,optaoa)) -return hint,debrief,radiocall -end -function AIRBOSS:_SpeedCheck(playerData,speedopt) -if speedopt==nil then -return nil,nil -end -local speed=playerData.unit:GetVelocityMPS() -local lowscore,badscore=self:_GetGoodBadScore(playerData) -local _error=(speed-speedopt)/speedopt*100 -local radiocall=nil -local hint="" -if _error>badscore then -radiocall=self:_NewRadioCall(self.LSOCall.FAST,"AIRBOSS","") -elseif _error>lowscore then -radiocall=self:_NewRadioCall(self.LSOCall.FAST,"AIRBOSS","") -elseif _error<-badscore then -radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"AIRBOSS","") -elseif _error<-lowscore then -radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"AIRBOSS","") -else -hint=string.format("Good speed. ") -end -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -hint=hint..string.format("Optimal speed is %d knots.",UTILS.MpsToKnots(speedopt)) -elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then -hint="" -elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then -hint="" -end -local debrief=string.format("Speed %d knots = %d%% deviation from %d knots.",UTILS.MpsToKnots(speed),_error,UTILS.MpsToKnots(speedopt)) -return hint,debrief,radiocall -end -function AIRBOSS:_DistanceCheck(playerData,optdist) -if optdist==nil then -return nil,nil -end -local distance=playerData.unit:GetCoordinate():Get2DDistance(self:GetCoordinate()) -local lowscore,badscore=self:_GetGoodBadScore(playerData) -local _error=(distance-optdist)/optdist*100 -local hint -if _error>badscore then -hint=string.format("You're too far from the boat!") -elseif _error>lowscore then -hint=string.format("You're slightly too far from the boat.") -elseif _error<-badscore then -hint=string.format("You're too close to the boat!") -elseif _error<-lowscore then -hint=string.format("You're slightly too far from the boat.") -else -hint=string.format("Good distance to the boat.") -end -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -hint=hint..string.format(" Optimal distance is %.1f NM.",UTILS.MetersToNM(optdist)) -elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then -hint="" -elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then -hint="" -end -local debrief=string.format("Distance %.1f NM = %d%% deviation from %.1f NM.",UTILS.MetersToNM(distance),_error,UTILS.MetersToNM(optdist)) -return hint,debrief,nil -end -function AIRBOSS:_AddToDebrief(playerData,hint,step) -step=step or playerData.step -table.insert(playerData.debrief,{step=step,hint=hint}) -end -function AIRBOSS:_Debrief(playerData) -self:F(self.lid..string.format("Debriefing of player %s.",playerData.name)) -playerData.debriefschedulerID=nil -playerData.attitudemonitor=false -local grade,points,analysis=self:_LSOgrade(playerData) -if points and points>=0 then -table.insert(playerData.points,points) -end -local Points=0 -if playerData.landed and not playerData.unit:InAir()then -for _,_points in pairs(playerData.points)do -Points=Points+_points -end -Points=Points/#playerData.points -playerData.points={} -else -Points=points -end -local mygrade={} -mygrade.grade=grade -mygrade.points=points -mygrade.details=analysis -mygrade.wire=playerData.wire -mygrade.Tgroove=playerData.Tgroove -if playerData.landed and not playerData.unit:InAir()then -mygrade.finalscore=Points -end -mygrade.case=playerData.case -local windondeck=self:GetWindOnDeck() -mygrade.wind=tostring(UTILS.Round(UTILS.MpsToKnots(windondeck),1)) -mygrade.modex=playerData.onboard -mygrade.airframe=playerData.actype -mygrade.carriertype=self.carriertype -mygrade.carriername=self.alias -mygrade.theatre=self.theatre -mygrade.mitime=UTILS.SecondsToClock(timer.getAbsTime()) -mygrade.midate=UTILS.GetDCSMissionDate() -mygrade.osdate="n/a" -if os then -mygrade.osdate=os.date() -end -if playerData.trapon and self.trapsheet then -self:_SaveTrapSheet(playerData,mygrade) -end -table.insert(self.playerscores[playerData.name],mygrade) -self:LSOGrade(playerData,mygrade) -local text=string.format("%s %.1f PT - %s",grade,Points,analysis) -if Points==-1 then -text=string.format("%s n/a PT - Foul deck",grade,Points,analysis) -end -if not(playerData.wop or playerData.wofd)then -if playerData.wire and playerData.wire<=4 then -text=text..string.format(" %d-wire",playerData.wire) -end -if playerData.Tgroove and playerData.Tgroove<=360 and playerData.case<3 then -text=text..string.format("\nTime in the groove %.1f seconds: %s",playerData.Tgroove,self:_EvalGrooveTime(playerData)) -end -end -playerData.lastdebrief=UTILS.DeepCopy(playerData.debrief) -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -text=text..string.format("\nYour detailed debriefing can be found via the F10 radio menu.") -end -self:MessageToPlayer(playerData,text,"LSO","",30,true) -playerData.step=AIRBOSS.PatternStep.UNDEFINED -if playerData.wop then -if playerData.unit:IsAlive()then -local heading,distance -if playerData.case==1 or playerData.case==2 then -playerData.step=AIRBOSS.PatternStep.INITIAL -local initial=self:GetCoordinate():Translate(UTILS.NMToMeters(3.5),self:GetRadial(2,false,false,false)) -heading=playerData.unit:GetCoordinate():HeadingTo(initial) -distance=playerData.unit:GetCoordinate():Get2DDistance(initial) -elseif playerData.case==3 then -playerData.step=AIRBOSS.PatternStep.BULLSEYE -local zone=self:_GetZoneBullseye(playerData.case) -heading=playerData.unit:GetCoordinate():HeadingTo(zone:GetCoordinate()) -distance=playerData.unit:GetCoordinate():Get2DDistance(zone:GetCoordinate()) -end -local text=string.format("fly heading %03d° for %d NM to re-enter the pattern.",heading,UTILS.MetersToNM(distance)) -self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,5) -else -self:E(self.lid..string.format("ERROR: Player unit not alive!")) -end -elseif playerData.wofd then -if playerData.unit:InAir()then -playerData.step=AIRBOSS.PatternStep.BOLTER -else -self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD) -local text=string.format("deck was fouled but you landed anyway. Airboss wants to talk to you!") -self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,3) -end -elseif playerData.owo then -if playerData.unit:InAir()then -playerData.step=AIRBOSS.PatternStep.BOLTER -else -self:E(self.lid.."ERROR: player landed when OWO was issues. This should not happen. Please report!") -self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD) -end -elseif playerData.waveoff then -if playerData.unit:InAir()then -playerData.step=AIRBOSS.PatternStep.BOLTER -else -self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD) -local text=string.format("you were waved off but landed anyway. Airboss wants to talk to you!") -self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,3) -end -elseif playerData.boltered then -if playerData.unit:InAir()then -playerData.step=AIRBOSS.PatternStep.BOLTER -end -elseif playerData.landed then -if not playerData.unit:InAir()then -self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD) -end -else -self:MessageToPlayer(playerData,"Undefined state after landing! Please report.","ERROR",nil,20) -playerData.step=AIRBOSS.PatternStep.UNDEFINED -end -if playerData.landed and not playerData.unit:InAir()then -self:_RecoveredElement(playerData.unit) -self:_CheckSectionRecovered(playerData) -end -playerData.passes=playerData.passes+1 -self:_StepHint(playerData) -self:_InitPlayer(playerData,playerData.step) -MESSAGE:New(string.format("Player step %s.",playerData.step),5,"DEBUG"):ToAllIf(self.Debug) -if self.autosave and mygrade.finalscore then -self:Save(self.autosavepath,self.autosavefile) -end -end -function AIRBOSS:_CheckCollisionCoord(coordto,coordfrom) -local dx=100 -local d=0 -if coordfrom then -d=0 -else -d=250 -coordfrom=self:GetCoordinate():Translate(d,self:GetHeading()) -end -local dmax=coordfrom:Get2DDistance(coordto) -local direction=coordfrom:HeadingTo(coordto) -local clear=true -while d<=dmax do -local cp=coordfrom:Translate(d,direction) -if not cp:IsSurfaceTypeWater()then -if self.Debug then -local st=cp:GetSurfaceType() -cp:MarkToAll(string.format("Collision check surface type %d",st)) -end -clear=false -break -end -d=d+dx -end -local text="" -if clear then -text=string.format("Path into direction %03d° is clear for the next %.1f NM.",direction,UTILS.MetersToNM(d)) -else -text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.",UTILS.MetersToNM(d),direction) -end -self:T2(self.lid..text) -return not clear,d -end -function AIRBOSS:_CheckFreePathToNextWP(fromcoord) -fromcoord=fromcoord or self:GetCoordinate():Translate(250,self:GetHeading()) -local Nnextwp=math.min(self.currentwp+1,#self.waypoints) -local nextwp=self.waypoints[Nnextwp] -local collision=self:_CheckCollisionCoord(nextwp,fromcoord) -return collision -end -function AIRBOSS:_Pathfinder() -local hdg=self:GetHeading() -local cv=self:GetCoordinate() -local directions={-20,20,-30,30,-40,40,-50,50,-60,60,-70,70,-80,80,-90,90,-100,100} -for _,_direction in pairs(directions)do -local direction=hdg+_direction -local _,dfree=self:_CheckCollisionCoord(cv:Translate(UTILS.NMToMeters(20),direction),cv) -local distance=500 -while distance<=dfree do -local fromcoord=cv:Translate(distance,direction) -local collision=self:_CheckFreePathToNextWP(fromcoord) -self:T2(self.lid..string.format("Pathfinder d=%.1f m, direction=%03d°, collision=%s",distance,direction,tostring(collision))) -if not collision then -self:CarrierDetour(fromcoord) -return -end -distance=distance+500 -end -end -end -function AIRBOSS:CarrierResumeRoute(gotocoord) -AIRBOSS._ResumeRoute(self.carrier:GetGroup(),self,gotocoord) -return self -end -function AIRBOSS:CarrierDetour(coord,speed,uturn,uspeed,tcoord) -local pos0=self:GetCoordinate() -local vel0=self.carrier:GetVelocityKNOTS() -speed=speed or math.max(vel0,5) -local speedkmh=math.max(UTILS.KnotsToKmph(speed),UTILS.KnotsToKmph(2)) -local cspeedkmh=math.max(self.carrier:GetVelocityKMH(),UTILS.KnotsToKmph(10)) -local uspeedkmh=UTILS.KnotsToKmph(uspeed or speed) -local wp={} -table.insert(wp,pos0:WaypointGround(cspeedkmh)) -if tcoord then -table.insert(wp,tcoord:WaypointGround(cspeedkmh)) -end -table.insert(wp,coord:WaypointGround(speedkmh)) -if uturn then -table.insert(wp,pos0:WaypointGround(uspeedkmh)) -end -local group=self.carrier:GetGroup() -local TaskResumeRoute=group:TaskFunction("AIRBOSS._ResumeRoute",self) -group:SetTaskWaypoint(wp[#wp],TaskResumeRoute) -if self.Debug then -if tcoord then -tcoord:MarkToAll(string.format("Detour Turn Help WP. Speed %.1f knots",UTILS.KmphToKnots(cspeedkmh))) -end -coord:MarkToAll(string.format("Detour Waypoint. Speed %.1f knots",UTILS.KmphToKnots(speedkmh))) -if uturn then -pos0:MarkToAll(string.format("Detour U-turn WP. Speed %.1f knots",UTILS.KmphToKnots(uspeedkmh))) -end -end -self.detour=true -self.carrier:Route(wp) -end -function AIRBOSS:CarrierTurnIntoWind(time,vdeck,uturn) -local _,vwind=self:GetWind() -local vtot=math.max(vdeck-vwind,UTILS.KnotsToMps(2)) -local dist=vtot*time -local speedknots=UTILS.MpsToKnots(vtot) -local distNM=UTILS.MetersToNM(dist) -self:I(self.lid..string.format("Carrier steaming into the wind (%.1f kts). Distance=%.1f NM, Speed=%.1f knots, Time=%d sec.",UTILS.MpsToKnots(vwind),distNM,speedknots,time)) -local hiw=self:GetHeadingIntoWind() -local hdg=self:GetHeading() -local deltaH=self:_GetDeltaHeading(hdg,hiw) -local Cv=self:GetCoordinate() -local Ctiw=nil -local Csoo=nil -if deltaH<45 then -Csoo=Cv:Translate(750,hdg):Translate(750,hiw) -local hsw=self:GetHeadingIntoWind(false,Csoo) -Ctiw=Csoo:Translate(dist,hsw) -elseif deltaH<90 then -Csoo=Cv:Translate(900,hdg):Translate(900,hiw) -local hsw=self:GetHeadingIntoWind(false,Csoo) -Ctiw=Csoo:Translate(dist,hsw) -elseif deltaH<135 then -Csoo=Cv:Translate(1100,hdg-90):Translate(1000,hiw) -local hsw=self:GetHeadingIntoWind(false,Csoo) -Ctiw=Csoo:Translate(dist,hsw) -else -Csoo=Cv:Translate(1200,hdg-90):Translate(1000,hiw) -local hsw=self:GetHeadingIntoWind(false,Csoo) -Ctiw=Csoo:Translate(dist,hsw) -end -self.Creturnto=self:GetCoordinate() -local nextwp=self:_GetNextWaypoint() -local vdownwind=UTILS.MpsToKnots(nextwp:GetVelocity()) -if vdownwind<1 then -vdownwind=10 -end -self:CarrierDetour(Ctiw,speedknots,uturn,vdownwind,Csoo) -self.turnintowind=true -return self -end -function AIRBOSS:_GetNextWaypoint() -local Nextwp=nil -if self.currentwp==#self.waypoints then -Nextwp=1 -else -Nextwp=self.currentwp+1 -end -local text=string.format("Current WP=%d/%d, next WP=%d",self.currentwp,#self.waypoints,Nextwp) -self:T2(self.lid..text) -local nextwp=self.waypoints[Nextwp] -return nextwp,Nextwp -end -function AIRBOSS:_InitWaypoints() -local Waypoints=self.carrier:GetGroup():GetTemplateRoutePoints() -self.waypoints={} -for i,point in ipairs(Waypoints)do -local coord=COORDINATE:New(point.x,point.alt,point.y) -coord:SetVelocity(point.speed) -table.insert(self.waypoints,coord) -if self.Debug then -coord:MarkToAll(string.format("Carrier Waypoint %d, Speed=%.1f knots",i,UTILS.MpsToKnots(point.speed))) -end -end -return self -end -function AIRBOSS:_PatrolRoute(n) -local nextWP,N=self:_GetNextWaypoint() -n=n or N -local CarrierGroup=self.carrier:GetGroup() -local Waypoints={} -local wp=self:GetCoordinate():WaypointGround(CarrierGroup:GetVelocityKMH()) -table.insert(Waypoints,wp) -for i=n,#self.waypoints do -local coord=self.waypoints[i] -local wp=coord:WaypointGround(UTILS.MpsToKmph(coord.Velocity)) -local TaskPassingWP=CarrierGroup:TaskFunction("AIRBOSS._PassingWaypoint",self,i,#self.waypoints) -CarrierGroup:SetTaskWaypoint(wp,TaskPassingWP) -table.insert(Waypoints,wp) -end -CarrierGroup:Route(Waypoints) -return self -end -function AIRBOSS:_GetETAatNextWP() -local cwp=self.currentwp -local tnow=timer.getAbsTime() -local p=self:GetCoordinate() -local v=self.carrier:GetVelocityMPS() -local nextWP=self:_GetNextWaypoint() -local s=p:Get2DDistance(nextWP) -local t=s/v -local eta=t+tnow -return eta -end -function AIRBOSS:_CheckCarrierTurning() -local vNew=self.carrier:GetOrientationX() -local vLast=self.Corientlast -vNew.y=0;vLast.y=0 -local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast))) -self.Corientlast=vNew -local turning=math.abs(deltaLast)>=1 -if self.turning and not turning then -local FB=self:GetFinalBearing(true) -self:_MarshalCallNewFinalBearing(FB) -end -if turning and not self.turning then -local hdg -if self.turnintowind then -hdg=self:GetHeadingIntoWind(false) -else -hdg=self:GetCoordinate():HeadingTo(self:_GetNextWaypoint()) -end -hdg=hdg-self.magvar -if hdg<0 then -hdg=360+hdg -end -self:_MarshalCallCarrierTurnTo(hdg) -end -self.turning=turning -end -function AIRBOSS:_CheckPatternUpdate() -local dTPupdate=10*60 -local Dupdate=UTILS.NMToMeters(2.5) -local Hupdate=5 -local dt=timer.getTime()-self.Tpupdate -if dt=Hupdate then -self:T(self.lid..string.format("Carrier heading changed by %d°.",deltaHeading)) -Hchange=true -end -local pos=self:GetCoordinate() -local dist=pos:Get2DDistance(self.Cposition) -local Dchange=false -if dist>=Dupdate then -self:T(self.lid..string.format("Carrier position changed by %.1f NM.",UTILS.MetersToNM(dist))) -Dchange=true -end -if Hchange or Dchange then -for _,_flight in pairs(self.Qmarshal)do -local flight=_flight -if flight.ai then -self:_MarshalAI(flight,flight.flag) -end -end -self.Corientation=vNew -self.Cposition=pos -self.Tpupdate=timer.getTime() -end -end -function AIRBOSS._PassingWaypoint(group,airboss,i,final) -local text=string.format("Group %s passing waypoint %d of %d.",group:GetName(),i,final) -if airboss.Debug and false then -local pos=group:GetCoordinate() -pos:SmokeRed() -local MarkerID=pos:MarkToAll(string.format("Group %s reached waypoint %d",group:GetName(),i)) -end -MESSAGE:New(text,10):ToAllIf(airboss.Debug) -airboss:T(airboss.lid..text) -airboss.currentwp=i -airboss:PassingWaypoint(i) -if i==final and final>1 and airboss.adinfinitum then -airboss:_PatrolRoute() -end -end -function AIRBOSS._ResumeRoute(group,airboss,gotocoord) -local nextwp,Nextwp=airboss:_GetNextWaypoint() -local speedkmh=nextwp.Velocity*3.6 -if speedkmh<1 then -speedkmh=UTILS.KnotsToKmph(10) -end -local waypoints={} -local c0=group:GetCoordinate() -local wp0=c0:WaypointGround(speedkmh) -table.insert(waypoints,wp0) -if gotocoord then -local headingto=c0:HeadingTo(gotocoord) -local hdg1=airboss:GetHeading() -local hdg2=c0:HeadingTo(gotocoord) -local delta=airboss:_GetDeltaHeading(hdg1,hdg2) -if delta>90 then -local turnradius=UTILS.NMToMeters(3) -local gotocoordh=c0:Translate(turnradius,hdg1+45) -local wp=gotocoordh:WaypointGround(speedkmh) -table.insert(waypoints,wp) -gotocoordh=c0:Translate(turnradius,hdg1+90) -wp=gotocoordh:WaypointGround(speedkmh) -table.insert(waypoints,wp) -end -local wp1=gotocoord:WaypointGround(speedkmh) -table.insert(waypoints,wp1) -end -local text=string.format("Carrier is resuming route. Next waypoint %d, Speed=%.1f knots.",Nextwp,UTILS.KmphToKnots(speedkmh)) -MESSAGE:New(text,10):ToAllIf(airboss.Debug) -airboss:I(airboss.lid..text) -for i=Nextwp,#airboss.waypoints do -local coord=airboss.waypoints[i] -local speed=coord.Velocity*3.6 -if speed<1 then -speed=UTILS.KnotsToKmph(10) -end -local wp=coord:WaypointGround(speed) -local TaskPassingWP=group:TaskFunction("AIRBOSS._PassingWaypoint",airboss,i,#airboss.waypoints) -group:SetTaskWaypoint(wp,TaskPassingWP) -table.insert(waypoints,wp) -end -airboss.turnintowind=false -airboss.detour=false -group:Route(waypoints) -end -function AIRBOSS._ReachedHoldingZone(group,airboss,flight) -local text=string.format("Flight %s reached holding zone.",group:GetName()) -MESSAGE:New(text,10):ToAllIf(airboss.Debug) -airboss:T(airboss.lid..text) -if airboss.Debug then -group:GetCoordinate():MarkToAll(text) -end -if flight then -flight.holding=true -flight.time=timer.getAbsTime() -end -end -function AIRBOSS._TaskFunctionMarshalAI(group,airboss,flight) -local text=string.format("Flight %s is send to marshal.",group:GetName()) -MESSAGE:New(text,10):ToAllIf(airboss.Debug) -airboss:T(airboss.lid..text) -local stack=airboss:_GetFreeStack(flight.ai) -if stack then -airboss:_MarshalAI(flight,stack) -else -if not airboss:_InQueue(airboss.Qwaiting,flight.group)then -airboss:_WaitAI(flight) -end -end -if flight.refueling==true then -airboss:I(airboss.lid..string.format("Flight group %s finished refueling task.",flight.groupname)) -end -flight.refueling=false -end -function AIRBOSS:_GetACNickname(actype) -local nickname="unknown" -if actype==AIRBOSS.AircraftCarrier.A4EC then -nickname="Skyhawk" -elseif actype==AIRBOSS.AircraftCarrier.T45C then -nickname="Goshawk" -elseif actype==AIRBOSS.AircraftCarrier.AV8B then -nickname="Harrier" -elseif actype==AIRBOSS.AircraftCarrier.E2D then -nickname="Hawkeye" -elseif actype==AIRBOSS.AircraftCarrier.F14A_AI or actype==AIRBOSS.AircraftCarrier.F14A or actype==AIRBOSS.AircraftCarrier.F14B then -nickname="Tomcat" -elseif actype==AIRBOSS.AircraftCarrier.FA18C or actype==AIRBOSS.AircraftCarrier.HORNET then -nickname="Hornet" -elseif actype==AIRBOSS.AircraftCarrier.S3B or actype==AIRBOSS.AircraftCarrier.S3BTANKER then -nickname="Viking" -end -return nickname -end -function AIRBOSS:_GetOnboardNumberPlayer(group) -return self:_GetOnboardNumbers(group,true) -end -function AIRBOSS:_GetOnboardNumbers(group,playeronly) -local groupname=group:GetName() -local text=string.format("Onboard numbers of group %s:",groupname) -local units=group:GetTemplate().units -local numbers={} -for _,unit in pairs(units)do -local n=tostring(unit.onboard_num) -local name=unit.name -local skill=unit.skill or"Unknown" -text=text..string.format("\n- unit %s: onboard #=%s skill=%s",name,n,tostring(skill)) -if playeronly and skill=="Client"or skill=="Player"then -return n -end -numbers[name]=n -end -self:T2(self.lid..text) -return numbers -end -function AIRBOSS:_GetTowerFrequency() -self.TowerFreq=0 -local striketemplate=self.carrier:GetGroup():GetTemplate() -for _,unit in pairs(striketemplate.units)do -if self.carrier:GetName()==unit.name then -self.TowerFreq=unit.frequency/1000000 -return -end -end -end -function AIRBOSS:_GetGoodBadScore(playerData) -local lowscore -local badscore -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -lowscore=10 -badscore=20 -elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then -lowscore=5 -badscore=10 -elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then -lowscore=2.5 -badscore=5 -end -return lowscore,badscore -end -function AIRBOSS:_IsCarrierAircraft(unit) -local aircrafttype=unit:GetTypeName() -if aircrafttype==AIRBOSS.AircraftCarrier.AV8B then -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -return true -else -return false -end -end -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -if aircrafttype~=AIRBOSS.AircraftCarrier.AV8B then -return false -end -end -for _,actype in pairs(AIRBOSS.AircraftCarrier)do -if actype==aircrafttype then -return true -end -end -return false -end -function AIRBOSS:_IsHumanUnit(unit) -local playerunit=self:_GetPlayerUnitAndName(unit:GetName()) -if playerunit then -return true -else -return false -end -end -function AIRBOSS:_IsHuman(group) -local units=group:GetUnits() -for _,_unit in pairs(units)do -local human=self:_IsHumanUnit(_unit) -if human then -return true -end -end -return false -end -function AIRBOSS:_GetFuelState(unit) -local fuel=unit:GetFuel() -local maxfuel=self:_GetUnitMasses(unit) -local fuelstate=fuel*maxfuel -self:T2(self.lid..string.format("Unit %s fuel state = %.1f kg = %.1f lbs",unit:GetName(),fuelstate,UTILS.kg2lbs(fuelstate))) -return UTILS.kg2lbs(fuelstate) -end -function AIRBOSS:_GetAngels(alt) -if alt then -local angels=UTILS.Round(UTILS.MetersToFeet(alt)/1000,0) -return angels -else -return 0 -end -end -function AIRBOSS:_GetUnitMasses(unit) -local Desc=unit:GetDesc() -local massfuel=Desc.fuelMassMax or 0 -local massempty=Desc.massEmpty or 0 -local massmax=Desc.massMax or 0 -local masscargo=massmax-massfuel-massempty -self:T2(self.lid..string.format("Unit %s mass fuel=%.1f kg, empty=%.1f kg, max=%.1f kg, cargo=%.1f kg",unit:GetName(),massfuel,massempty,massmax,masscargo)) -return massfuel,massempty,massmax,masscargo -end -function AIRBOSS:_GetPlayerDataUnit(unit) -if unit:IsAlive()then -local unitname=unit:GetName() -local playerunit,playername=self:_GetPlayerUnitAndName(unitname) -if playerunit and playername then -return self.players[playername] -end -end -return nil -end -function AIRBOSS:_GetPlayerDataGroup(group) -local units=group:GetUnits() -for _,unit in pairs(units)do -local playerdata=self:_GetPlayerDataUnit(unit) -if playerdata then -return playerdata -end -end -return nil -end -function AIRBOSS:_GetPlayerUnit(_unitName) -for _,_player in pairs(self.players)do -local player=_player -if player.unit and player.unit:GetName()==_unitName then -self:T(self.lid..string.format("Found player=%s unit=%s in players table.",tostring(player.name),tostring(_unitName))) -return player.unit,player.name -end -end -return nil,nil -end -function AIRBOSS:_GetPlayerUnitAndName(_unitName) -self:F2(_unitName) -if _unitName~=nil then -local u,pn=self:_GetPlayerUnit(_unitName) -if u and pn then -return u,pn -end -local DCSunit=Unit.getByName(_unitName) -if DCSunit then -local playername=DCSunit:getPlayerName() -local unit=UNIT:Find(DCSunit) -self:T2({DCSunit=DCSunit,unit=unit,playername=playername}) -if DCSunit and unit and playername then -self:T(self.lid..string.format("Found DCS unit %s with player %s.",tostring(_unitName),tostring(playername))) -return unit,playername -end -end -end -return nil,nil -end -function AIRBOSS:GetCoalition() -return self.carrier:GetCoalition() -end -function AIRBOSS:GetCoordinate() -return self.carrier:GetCoord() -end -function AIRBOSS:GetCoord() -return self.carrier:GetCoord() -end -function AIRBOSS:_GetStaticWeather() -local weather=env.mission.weather -local clouds=weather.clouds -local visibility=weather.visibility.distance -local dust=nil -if weather.enable_dust==true then -dust=weather.dust_density -end -local fog=nil -if weather.enable_fog==true then -fog=weather.fog -end -return clouds,visibility,fog,dust -end -function AIRBOSS._CheckRadioQueueT(param,time) -AIRBOSS._CheckRadioQueue(param.airboss,param.radioqueue,param.name) -return time+0.05 -end -function AIRBOSS:_CheckRadioQueue(radioqueue,name) -if#radioqueue==0 then -if name=="LSO"then -self:T(self.lid..string.format("Stopping LSO radio queue.")) -self.radiotimer:Stop(self.RQLid) -self.RQLid=nil -elseif name=="MARSHAL"then -self:T(self.lid..string.format("Stopping Marshal radio queue.")) -self.radiotimer:Stop(self.RQMid) -self.RQMid=nil -end -return -end -local _time=timer.getAbsTime() -local playing=false -local next=nil -local _remove=nil -for i,_transmission in ipairs(radioqueue)do -local transmission=_transmission -if _time>=transmission.Tplay then -if transmission.isplaying then -if _time>=transmission.Tstarted+transmission.call.duration then -transmission.isplaying=false -_remove=i -if transmission.radio.alias=="LSO"then -self.TQLSO=_time -elseif transmission.radio.alias=="MARSHAL"then -self.TQMarshal=_time -end -else -playing=true -end -else -local Tlast=nil -if transmission.interval then -if transmission.radio.alias=="LSO"then -Tlast=self.TQLSO -elseif transmission.radio.alias=="MARSHAL"then -Tlast=self.TQMarshal -end -end -if transmission.interval==nil then -if next==nil then -next=transmission -end -else -if _time-Tlast>=transmission.interval then -next=transmission -else -end -end -if next or Tlast then -break -end -end -else -end -end -if next~=nil and not playing then -self:Broadcast(next.radio,next.call,next.loud) -next.isplaying=true -next.Tstarted=_time -end -if _remove then -table.remove(radioqueue,_remove) -end -return -end -function AIRBOSS:RadioTransmission(radio,call,loud,delay,interval,click,pilotcall) -self:F2({radio=radio,call=call,loud=loud,delay=delay,interval=interval,click=click}) -if radio==nil or call==nil then -return -end -local transmission={} -transmission.radio=radio -transmission.call=call -transmission.Tplay=timer.getAbsTime()+(delay or 0) -transmission.interval=interval -transmission.isplaying=false -transmission.Tstarted=nil -transmission.loud=loud and call.loud -if self:_IsOnboard(call.modexsender)then -self:_Number2Radio(radio,call.modexsender,delay,0.3,pilotcall) -end -if self:_IsOnboard(call.modexreceiver)then -self:_Number2Radio(radio,call.modexreceiver,delay,0.3,pilotcall) -end -local caller="" -if radio.alias=="LSO"then -table.insert(self.RQLSO,transmission) -caller="LSOCall" -if not self.RQLid then -self:T(self.lid..string.format("Starting LSO radio queue.")) -self.RQLid=self.radiotimer:Schedule(nil,AIRBOSS._CheckRadioQueue,{self,self.RQLSO,"LSO"},0.02,0.05) -end -elseif radio.alias=="MARSHAL"then -table.insert(self.RQMarshal,transmission) -caller="MarshalCall" -if not self.RQMid then -self:T(self.lid..string.format("Starting Marhal radio queue.")) -self.RQMid=self.radiotimer:Schedule(nil,AIRBOSS._CheckRadioQueue,{self,self.RQMarshal,"MARSHAL"},0.02,0.05) -end -end -if click then -self:RadioTransmission(radio,self[caller].CLICK,false,delay) -end -end -function AIRBOSS:_NeedsSubtitle(call) -if call.file==self.MarshalCall.NOISE.file or call.file==self.LSOCall.NOISE.file then -return true -else -return false -end -end -function AIRBOSS:Broadcast(radio,call,loud) -self:F(call) -if not self.usersoundradio then -local sender=self:_GetRadioSender(radio) -local filename=self:_RadioFilename(call,loud,radio.alias) -local subtitle=self:_RadioSubtitle(radio,call,loud) -self:T({filename=filename,subtitle=subtitle}) -if sender then -self:T(self.lid..string.format("Broadcasting from aircraft %s",sender:GetName())) -local commandFrequency={ -id="SetFrequency", -params={ -frequency=radio.frequency*1000000, -modulation=radio.modulation, -}} -local commandTransmit={ -id="TransmitMessage", -params={ -file=filename, -duration=call.subduration or 5, -subtitle=subtitle, -loop=false, -}} -sender:SetCommand(commandFrequency) -sender:SetCommand(commandTransmit) -else -self:T(self.lid..string.format("Broadcasting from carrier via trigger.action.radioTransmission().")) -local vec3=self.carrier:GetPositionVec3() -trigger.action.radioTransmission(filename,vec3,radio.modulation,false,radio.frequency*1000000,100) -for _,_player in pairs(self.players)do -local playerData=_player -if playerData.unit:IsInZone(self.zoneCCA)and playerData.actype~=AIRBOSS.AircraftCarrier.A4EC then -if playerData.subtitles or self:_NeedsSubtitle(call)then -if radio.alias=="MARSHAL"or(radio.alias=="LSO"and self:_InQueue(self.Qpattern,playerData.group))then -self:MessageToPlayer(playerData,subtitle,nil,"",call.subduration or 5) -end -end -end -end -end -end -for _,_player in pairs(self.players)do -local playerData=_player -if self.usersoundradio or playerData.actype==AIRBOSS.AircraftCarrier.A4EC then -if radio.alias=="MARSHAL"or(radio.alias=="LSO"and self:_InQueue(self.Qpattern,playerData.group))then -self:Sound2Player(playerData,radio,call,loud) -end -end -end -end -function AIRBOSS:Sound2Player(playerData,radio,call,loud,delay) -if playerData.unit:IsInZone(self.zoneCCA)and call then -local filename=self:_RadioFilename(call,loud,radio.alias) -local subtitle=self:_RadioSubtitle(radio,call,loud) -USERSOUND:New(filename):ToGroup(playerData.group,delay) -if playerData.subtitles or self:_NeedsSubtitle(call)then -self:MessageToPlayer(playerData,subtitle,nil,"",call.subduration,false,delay) -end -end -end -function AIRBOSS:_RadioSubtitle(radio,call,loud) -if call==nil or call.subtitle==nil or call.subtitle==""then -return"" -end -local sender=call.sender or radio.alias -if call.modexsender then -sender=call.modexsender -end -local receiver=call.modexreceiver or"" -local subtitle=string.format("%s: %s",sender,call.subtitle) -if receiver and receiver~=""then -subtitle=string.format("%s: %s, %s",sender,receiver,call.subtitle) -end -local lastchar=string.sub(subtitle,-1) -if loud then -if lastchar=="."or lastchar=="!"then -subtitle=string.sub(subtitle,1,-1) -end -subtitle=subtitle.."!" -else -if lastchar=="!"then -elseif lastchar=="."then -else -subtitle=subtitle.."." -end -end -return subtitle -end -function AIRBOSS:_RadioFilename(call,loud,channel) -local prefix=call.file or"" -local suffix=call.suffix or"ogg" -local path=self.soundfolder or"l10n/DEFAULT/" -if string.find(call.file,"LSO-")and channel and(channel=="LSO"or channel=="LSOCall")then -path=self.soundfolderLSO or path -end -if string.find(call.file,"MARSHAL-")and channel and(channel=="MARSHAL"or channel=="MarshalCall")then -path=self.soundfolderMSH or path -end -if loud then -prefix=prefix.."_Loud" -end -local filename=string.format("%s%s.%s",path,prefix,suffix) -return filename -end -function AIRBOSS:MessageToPlayer(playerData,message,sender,receiver,duration,clear,delay) -if playerData and message and message~=""then -duration=duration or self.Tmessage -local text -if receiver and receiver==""then -text=string.format("%s",message) -else -receiver=receiver or playerData.onboard -text=string.format("%s, %s",receiver,message) -end -self:T(self.lid..text) -if delay and delay>0 then -self:ScheduleOnce(delay,self.MessageToPlayer,self,playerData,message,sender,receiver,duration,clear) -else -local wait=0 -if receiver==playerData.onboard then -if sender and(sender=="LSO"or sender=="MARSHAL"or sender=="AIRBOSS")then -wait=wait+self:_Number2Sound(playerData,sender,receiver) -end -end -if string.find(text:lower(),"negative")then -local filename=self:_RadioFilename(self.MarshalCall.NEGATIVE,false,"MARSHAL") -USERSOUND:New(filename):ToGroup(playerData.group,wait) -wait=wait+self.MarshalCall.NEGATIVE.duration -end -if string.find(text:lower(),"affirm")then -local filename=self:_RadioFilename(self.MarshalCall.AFFIRMATIVE,false,"MARSHAL") -USERSOUND:New(filename):ToGroup(playerData.group,wait) -wait=wait+self.MarshalCall.AFFIRMATIVE.duration -end -if string.find(text:lower(),"roger")then -local filename=self:_RadioFilename(self.MarshalCall.ROGER,false,"MARSHAL") -USERSOUND:New(filename):ToGroup(playerData.group,wait) -wait=wait+self.MarshalCall.ROGER.duration -end -if wait>0 then -local filename=self:_RadioFilename(self.MarshalCall.CLICK) -USERSOUND:New(filename):ToGroup(playerData.group,wait) -end -if playerData.client then -MESSAGE:New(text,duration,sender,clear):ToClient(playerData.client) -end -end -end -end -function AIRBOSS:MessageToPattern(message,sender,receiver,duration,clear,delay) -local call=self:_NewRadioCall(self.LSOCall.NOISE,sender or"LSO",message,duration,receiver,sender) -self:RadioTransmission(self.LSORadio,call,false,delay,nil,true) -end -function AIRBOSS:MessageToMarshal(message,sender,receiver,duration,clear,delay) -local call=self:_NewRadioCall(self.MarshalCall.NOISE,sender or"MARSHAL",message,duration,receiver,sender) -self:RadioTransmission(self.MarshalRadio,call,false,delay,nil,true) -end -function AIRBOSS:_NewRadioCall(call,sender,subtitle,subduration,modexreceiver,modexsender) -local newcall=UTILS.DeepCopy(call) -newcall.sender=sender -newcall.subtitle=subtitle or call.subtitle -newcall.subduration=subduration or self.Tmessage -if self:_IsOnboard(modexreceiver)then -newcall.modexreceiver=modexreceiver -end -if self:_IsOnboard(modexsender)then -newcall.modexsender=modexsender -end -return newcall -end -function AIRBOSS:_GetRadioSender(radio) -local sender=nil -if self.senderac then -sender=UNIT:FindByName(self.senderac) -end -if radio.alias=="MARSHAL"then -if self.radiorelayMSH then -sender=UNIT:FindByName(self.radiorelayMSH) -end -end -if radio.alias=="LSO"then -if self.radiorelayLSO then -sender=UNIT:FindByName(self.radiorelayLSO) -end -end -if sender and sender:IsAlive()and sender:IsAir()then -return sender -end -return nil -end -function AIRBOSS:_IsOnboard(text) -if text==nil then -return false -end -if text=="99"then -return true -end -for _,_flight in pairs(self.flights)do -local flight=_flight -for _,onboard in pairs(flight.onboardnumbers)do -if text==onboard then -return true -end -end -end -return false -end -function AIRBOSS:_Number2Sound(playerData,sender,number,delay) -delay=delay or 0 -local function _split(str) -local chars={} -for i=1,#str do -local c=str:sub(i,i) -table.insert(chars,c) -end -return chars -end -local Sender -if sender=="LSO"then -Sender="LSOCall" -elseif sender=="MARSHAL"or sender=="AIRBOSS"then -Sender="MarshalCall" -else -self:E(self.lid..string.format("ERROR: Unknown radio sender %s!",tostring(sender))) -return -end -local numbers=_split(number) -local wait=0 -for i=1,#numbers do -local n=numbers[i] -local N=string.format("N%s",n) -local call=self[Sender][N] -local filename=self:_RadioFilename(call,false,Sender) -USERSOUND:New(filename):ToGroup(playerData.group,delay+wait) -wait=wait+call.duration -end -return wait -end -function AIRBOSS:_Number2Radio(radio,number,delay,interval,pilotcall) -local function _split(str) -local chars={} -for i=1,#str do -local c=str:sub(i,i) -table.insert(chars,c) -end -return chars -end -local Sender="" -if radio.alias=="LSO"then -Sender="LSOCall" -elseif radio.alias=="MARSHAL"then -Sender="MarshalCall" -else -self:E(self.lid..string.format("ERROR: Unknown radio alias %s!",tostring(radio.alias))) -end -if pilotcall then -Sender="PilotCall" -end -local numbers=_split(number) -local wait=0 -for i=1,#numbers do -local n=numbers[i] -local N=string.format("N%s",n) -local call=self[Sender][N] -if interval and i==1 then -self:RadioTransmission(radio,call,false,delay,interval) -else -self:RadioTransmission(radio,call,false,delay) -end -wait=wait+call.duration -end -return wait -end -function AIRBOSS:_LSOCallAircraftBall(modex,nickname,fuelstate) -local text=string.format("%s Ball, %.1f.",nickname,fuelstate) -self:I(self.lid..text) -local NICKNAME=nickname:upper() -local FS=UTILS.Split(string.format("%.1f",fuelstate),".") -local call=self:_NewRadioCall(self.PilotCall[NICKNAME],modex,text,self.Tmessage,nil,modex) -self:RadioTransmission(self.LSORadio,call,nil,nil,nil,nil,true) -self:RadioTransmission(self.LSORadio,self.PilotCall.BALL,nil,nil,nil,nil,true) -self:_Number2Radio(self.LSORadio,FS[1],nil,nil,true) -self:RadioTransmission(self.LSORadio,self.PilotCall.POINT,nil,nil,nil,nil,true) -self:_Number2Radio(self.LSORadio,FS[2],nil,nil,true) -self:RadioTransmission(self.LSORadio,self.LSOCall.CLICK) -end -function AIRBOSS:_MarshalCallGasAtTanker(modex) -local text=string.format("Bingo fuel! Going for gas at the recovery tanker.") -self:I(self.lid..text) -local call=self:_NewRadioCall(self.PilotCall.BINGOFUEL,modex,text,self.Tmessage,nil,modex) -self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,nil,true) -self:RadioTransmission(self.MarshalRadio,self.PilotCall.GASATTANKER,nil,nil,nil,true,true) -end -function AIRBOSS:_MarshalCallGasAtDivert(modex,divertname) -local text=string.format("Bingo fuel! Going for gas at divert field %s.",divertname) -self:I(self.lid..text) -local call=self:_NewRadioCall(self.PilotCall.BINGOFUEL,modex,text,self.Tmessage,nil,modex) -self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,nil,true) -self:RadioTransmission(self.MarshalRadio,self.PilotCall.GASATDIVERT,nil,nil,nil,true,true) -end -function AIRBOSS:_MarshalCallRecoveryStopped(case) -local text=string.format("Case %d recovery ops are stopped. Deck is closed.",case) -self:I(self.lid..text) -local call=self:_NewRadioCall(self.MarshalCall.CASE,"AIRBOSS",text,self.Tmessage,"99") -self:RadioTransmission(self.MarshalRadio,call) -self:_Number2Radio(self.MarshalRadio,tostring(case)) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.RECOVERYOPSSTOPPED,nil,nil,0.2) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DECKCLOSED,nil,nil,nil,true) -end -function AIRBOSS:_MarshalCallRecoveryPausedUntilFurtherNotice() -local call=self:_NewRadioCall(self.MarshalCall.RECOVERYPAUSEDNOTICE,"AIRBOSS",nil,self.Tmessage,"99") -self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,true) -end -function AIRBOSS:_MarshalCallRecoveryPausedResumedAt(clock) -local _clock=UTILS.Split(clock,"+") -local CT=UTILS.Split(_clock[1],":") -local text=string.format("aircraft recovery is paused and will be resumed at %s.",clock) -self:I(self.lid..text) -local call=self:_NewRadioCall(self.MarshalCall.RECOVERYPAUSEDRESUMED,"AIRBOSS",text,self.Tmessage,"99") -self:RadioTransmission(self.MarshalRadio,call) -self:_Number2Radio(self.MarshalRadio,CT[1]) -self:_Number2Radio(self.MarshalRadio,CT[2]) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.HOURS,nil,nil,nil,true) -end -function AIRBOSS:_MarshalCallClearedForRecovery(modex,case) -local text=string.format("you're cleared for Case %d recovery.",case) -self:I(self.lid..text) -local call=self:_NewRadioCall(self.MarshalCall.CLEAREDFORRECOVERY,"MARSHAL",text,self.Tmessage,modex) -local delay=2 -self:RadioTransmission(self.MarshalRadio,call,nil,delay) -self:_Number2Radio(self.MarshalRadio,tostring(case),delay) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.RECOVERY,nil,delay,nil,true) -end -function AIRBOSS:_MarshalCallResumeRecovery() -local call=self:_NewRadioCall(self.MarshalCall.RESUMERECOVERY,"AIRBOSS",nil,self.Tmessage,"99") -self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,true) -end -function AIRBOSS:_MarshalCallNewFinalBearing(FB) -local text=string.format("new final bearing %03d°.",FB) -self:I(self.lid..text) -local call=self:_NewRadioCall(self.MarshalCall.NEWFB,"AIRBOSS",text,self.Tmessage,"99") -self:RadioTransmission(self.MarshalRadio,call) -self:_Number2Radio(self.MarshalRadio,string.format("%03d",FB),nil,0.2) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES,nil,nil,nil,true) -end -function AIRBOSS:_MarshalCallCarrierTurnTo(hdg) -local text=string.format("carrier is now starting turn to heading %03d°.",hdg) -self:I(self.lid..text) -local call=self:_NewRadioCall(self.MarshalCall.CARRIERTURNTOHEADING,"AIRBOSS",text,self.Tmessage,"99") -self:RadioTransmission(self.MarshalRadio,call) -self:_Number2Radio(self.MarshalRadio,string.format("%03d",hdg),nil,0.2) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES,nil,nil,nil,true) -end -function AIRBOSS:_MarshalCallStackFull(modex,nwaiting) -local text=string.format("Marshal stack is currently full. Hold outside 10 NM zone and wait for further instructions. ") -if nwaiting==1 then -text=text..string.format("There is one flight ahead of you.") -elseif nwaiting>1 then -text=text..string.format("There are %d flights ahead of you.",nwaiting) -else -text=text..string.format("You are next in line.") -end -self:I(self.lid..text) -local call=self:_NewRadioCall(self.MarshalCall.STACKFULL,"AIRBOSS",text,self.Tmessage,modex) -self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,true) -end -function AIRBOSS:_MarshalCallRecoveryStart(case) -local radial=self:GetRadial(case,true,true,false) -local text=string.format("Starting aircraft recovery Case %d ops.",case) -if case>1 then -text=text..string.format(" Marshal radial %03d°.",radial) -end -self:T(self.lid..text) -local call=self:_NewRadioCall(self.MarshalCall.STARTINGRECOVERY,"AIRBOSS",text,self.Tmessage,"99") -self:RadioTransmission(self.MarshalRadio,call) -self:_Number2Radio(self.MarshalRadio,tostring(case),nil,0.1) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.OPS) -if case>1 then -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.MARSHALRADIAL) -self:_Number2Radio(self.MarshalRadio,string.format("%03d",radial),nil,0.2) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES,nil,nil,nil,true) -end -end -function AIRBOSS:_MarshalCallArrived(modex,case,brc,altitude,charlie,qfe) -self:F({modex=modex,case=case,brc=brc,altitude=altitude,charlie=charlie,qfe=qfe}) -local angels=self:_GetAngels(altitude) -local QFE=UTILS.Split(string.format("%.2f",qfe),".") -local clock=UTILS.Split(charlie,"+") -local CT=UTILS.Split(clock[1],":") -local text=string.format("Case %d, expected BRC %03d°, hold at angels %d. Expected Charlie Time %s. Altimeter %.2f. Report see me.",case,brc,angels,charlie,qfe) -self:I(self.lid..text) -local casecall=self:_NewRadioCall(self.MarshalCall.CASE,"MARSHAL",text,self.Tmessage,modex) -self:RadioTransmission(self.MarshalRadio,casecall) -self:_Number2Radio(self.MarshalRadio,tostring(case)) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.EXPECTED,nil,nil,0.5) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.BRC) -self:_Number2Radio(self.MarshalRadio,string.format("%03d",brc)) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.HOLDATANGELS,nil,nil,0.5) -self:_Number2Radio(self.MarshalRadio,tostring(angels)) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.EXPECTED,nil,nil,0.5) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.CHARLIETIME) -self:_Number2Radio(self.MarshalRadio,CT[1]) -self:_Number2Radio(self.MarshalRadio,CT[2]) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.HOURS) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.ALTIMETER,nil,nil,0.5) -self:_Number2Radio(self.MarshalRadio,QFE[1]) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.POINT) -self:_Number2Radio(self.MarshalRadio,QFE[2]) -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.REPORTSEEME,nil,nil,0.5,true) -end -function AIRBOSS:_AddF10Commands(_unitName) -self:F(_unitName) -local _unit,playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and playername then -local group=_unit:GetGroup() -local gid=group:GetID() -if group and gid then -if not self.menuadded[gid]then -self.menuadded[gid]=true -local _rootPath=nil -if AIRBOSS.MenuF10Root then -if self.menusingle then -_rootPath=AIRBOSS.MenuF10Root -else -_rootPath=missionCommands.addSubMenuForGroup(gid,self.alias,AIRBOSS.MenuF10Root) -end -else -if AIRBOSS.MenuF10[gid]==nil then -AIRBOSS.MenuF10[gid]=missionCommands.addSubMenuForGroup(gid,"Airboss") -end -if self.menusingle then -_rootPath=AIRBOSS.MenuF10[gid] -else -_rootPath=missionCommands.addSubMenuForGroup(gid,self.alias,AIRBOSS.MenuF10[gid]) -end -end -local _helpPath=missionCommands.addSubMenuForGroup(gid,"Help",_rootPath) -if self.menumarkzones then -local _markPath=missionCommands.addSubMenuForGroup(gid,"Mark Zones",_helpPath) -if self.menusmokezones then -missionCommands.addCommandForGroup(gid,"Smoke Pattern Zones",_markPath,self._MarkCaseZones,self,_unitName,false) -end -missionCommands.addCommandForGroup(gid,"Flare Pattern Zones",_markPath,self._MarkCaseZones,self,_unitName,true) -if self.menusmokezones then -missionCommands.addCommandForGroup(gid,"Smoke Marshal Zone",_markPath,self._MarkMarshalZone,self,_unitName,false) -end -missionCommands.addCommandForGroup(gid,"Flare Marshal Zone",_markPath,self._MarkMarshalZone,self,_unitName,true) -end -local _skillPath=missionCommands.addSubMenuForGroup(gid,"Skill Level",_helpPath) -missionCommands.addCommandForGroup(gid,"Flight Student",_skillPath,self._SetDifficulty,self,_unitName,AIRBOSS.Difficulty.EASY) -missionCommands.addCommandForGroup(gid,"Naval Aviator",_skillPath,self._SetDifficulty,self,_unitName,AIRBOSS.Difficulty.NORMAL) -missionCommands.addCommandForGroup(gid,"TOPGUN Graduate",_skillPath,self._SetDifficulty,self,_unitName,AIRBOSS.Difficulty.HARD) -missionCommands.addCommandForGroup(gid,"Hints On/Off",_skillPath,self._SetHintsOnOff,self,_unitName) -missionCommands.addCommandForGroup(gid,"My Status",_helpPath,self._DisplayPlayerStatus,self,_unitName) -missionCommands.addCommandForGroup(gid,"Attitude Monitor",_helpPath,self._DisplayAttitude,self,_unitName) -missionCommands.addCommandForGroup(gid,"Radio Check LSO",_helpPath,self._LSORadioCheck,self,_unitName) -missionCommands.addCommandForGroup(gid,"Radio Check Marshal",_helpPath,self._MarshalRadioCheck,self,_unitName) -missionCommands.addCommandForGroup(gid,"Subtitles On/Off",_helpPath,self._SubtitlesOnOff,self,_unitName) -missionCommands.addCommandForGroup(gid,"Trapsheet On/Off",_helpPath,self._TrapsheetOnOff,self,_unitName) -local _kneeboardPath=missionCommands.addSubMenuForGroup(gid,"Kneeboard",_rootPath) -local _resultsPath=missionCommands.addSubMenuForGroup(gid,"Results",_kneeboardPath) -missionCommands.addCommandForGroup(gid,"Greenie Board",_resultsPath,self._DisplayScoreBoard,self,_unitName) -missionCommands.addCommandForGroup(gid,"My LSO Grades",_resultsPath,self._DisplayPlayerGrades,self,_unitName) -missionCommands.addCommandForGroup(gid,"Last Debrief",_resultsPath,self._DisplayDebriefing,self,_unitName) -if self.skipperMenu then -local _skipperPath=missionCommands.addSubMenuForGroup(gid,"Skipper",_kneeboardPath) -local _menusetspeed=missionCommands.addSubMenuForGroup(gid,"Set Speed",_skipperPath) -missionCommands.addCommandForGroup(gid,"10 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,10) -missionCommands.addCommandForGroup(gid,"15 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,15) -missionCommands.addCommandForGroup(gid,"20 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,20) -missionCommands.addCommandForGroup(gid,"25 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,25) -missionCommands.addCommandForGroup(gid,"30 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,30) -local _menusetrtime=missionCommands.addSubMenuForGroup(gid,"Set Time",_skipperPath) -missionCommands.addCommandForGroup(gid,"15 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,15) -missionCommands.addCommandForGroup(gid,"30 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,30) -missionCommands.addCommandForGroup(gid,"45 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,45) -missionCommands.addCommandForGroup(gid,"60 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,60) -missionCommands.addCommandForGroup(gid,"90 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,90) -local _menusetrtime=missionCommands.addSubMenuForGroup(gid,"Set Marshal Radial",_skipperPath) -missionCommands.addCommandForGroup(gid,"+30°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,30) -missionCommands.addCommandForGroup(gid,"+15°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,15) -missionCommands.addCommandForGroup(gid,"0°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,0) -missionCommands.addCommandForGroup(gid,"-15°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,-15) -missionCommands.addCommandForGroup(gid,"-30°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,-30) -missionCommands.addCommandForGroup(gid,"U-turn On/Off",_skipperPath,self._SkipperRecoveryUturn,self,_unitName) -missionCommands.addCommandForGroup(gid,"Start CASE I",_skipperPath,self._SkipperStartRecovery,self,_unitName,1) -missionCommands.addCommandForGroup(gid,"Start CASE II",_skipperPath,self._SkipperStartRecovery,self,_unitName,2) -missionCommands.addCommandForGroup(gid,"Start CASE III",_skipperPath,self._SkipperStartRecovery,self,_unitName,3) -missionCommands.addCommandForGroup(gid,"Stop Recovery",_skipperPath,self._SkipperStopRecovery,self,_unitName) -end -missionCommands.addCommandForGroup(gid,"Carrier Info",_kneeboardPath,self._DisplayCarrierInfo,self,_unitName) -missionCommands.addCommandForGroup(gid,"Weather Report",_kneeboardPath,self._DisplayCarrierWeather,self,_unitName) -missionCommands.addCommandForGroup(gid,"Set Section",_kneeboardPath,self._SetSection,self,_unitName) -missionCommands.addCommandForGroup(gid,"Marshal Queue",_kneeboardPath,self._DisplayQueue,self,_unitName,"Marshal") -missionCommands.addCommandForGroup(gid,"Pattern Queue",_kneeboardPath,self._DisplayQueue,self,_unitName,"Pattern") -missionCommands.addCommandForGroup(gid,"Waiting Queue",_kneeboardPath,self._DisplayQueue,self,_unitName,"Waiting") -missionCommands.addCommandForGroup(gid,"Request Marshal",_rootPath,self._RequestMarshal,self,_unitName) -missionCommands.addCommandForGroup(gid,"Request Commence",_rootPath,self._RequestCommence,self,_unitName) -missionCommands.addCommandForGroup(gid,"Request Refueling",_rootPath,self._RequestRefueling,self,_unitName) -missionCommands.addCommandForGroup(gid,"Spinning",_rootPath,self._RequestSpinning,self,_unitName) -missionCommands.addCommandForGroup(gid,"Emergency Landing",_rootPath,self._RequestEmergency,self,_unitName) -missionCommands.addCommandForGroup(gid,"[Reset My Status]",_rootPath,self._ResetPlayerStatus,self,_unitName) -end -else -self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.",_unitName)) -end -else -self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.",_unitName)) -end -end -function AIRBOSS:_SkipperStartRecovery(_unitName,case) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text=string.format("affirm, Case %d recovery will start in 5 min for %d min. Wind on deck %d knots. U-turn=%s.",case,self.skipperTime,self.skipperSpeed,tostring(self.skipperUturn)) -if case>1 then -text=text..string.format(" Marshal radial %d°.",self.skipperOffset) -end -if self:IsRecovering()then -text="negative, carrier is already recovering." -self:MessageToPlayer(playerData,text,"AIRBOSS") -return -end -self:MessageToPlayer(playerData,text,"AIRBOSS") -local t0=timer.getAbsTime()+5*60 -local t9=t0+self.skipperTime*60 -local C0=UTILS.SecondsToClock(t0) -local C9=UTILS.SecondsToClock(t9) -self:AddRecoveryWindow(C0,C9,case,self.skipperOffset,true,self.skipperSpeed,self.skipperUturn) -end -end -end -function AIRBOSS:_SkipperStopRecovery(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text="roger, stopping recovery right away." -if not self:IsRecovering()then -text="negative, carrier is currently not recovering." -self:MessageToPlayer(playerData,text,"AIRBOSS") -return -end -self:MessageToPlayer(playerData,text,"AIRBOSS") -self:RecoveryStop() -end -end -end -function AIRBOSS:_SkipperRecoveryOffset(_unitName,offset) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text=string.format("roger, relative CASE II/III Marshal radial set to %d°.",offset) -self:MessageToPlayer(playerData,text,"AIRBOSS") -self.skipperOffset=offset -end -end -end -function AIRBOSS:_SkipperRecoveryTime(_unitName,time) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text=string.format("roger, manual recovery time set to %d min.",time) -self:MessageToPlayer(playerData,text,"AIRBOSS") -self.skipperTime=time -end -end -end -function AIRBOSS:_SkipperRecoverySpeed(_unitName,speed) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text=string.format("roger, wind on deck set to %d knots.",speed) -self:MessageToPlayer(playerData,text,"AIRBOSS") -self.skipperSpeed=speed -end -end -end -function AIRBOSS:_SkipperRecoveryUturn(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -self.skipperUturn=not self.skipperUturn -local text=string.format("roger, U-turn is now %s.",tostring(self.skipperUturn)) -self:MessageToPlayer(playerData,text,"AIRBOSS") -end -end -end -function AIRBOSS:_ResetPlayerStatus(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text="roger, status reset executed! You have been removed from all queues." -self:MessageToPlayer(playerData,text,"AIRBOSS") -self:_RemoveFlight(playerData) -if playerData.debriefschedulerID and self.Scheduler then -self.Scheduler:Stop(playerData.debriefschedulerID) -end -self:_InitPlayer(playerData) -end -end -end -function AIRBOSS:_RequestMarshal(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local inCCA=playerData.unit:IsInZone(self.zoneCCA) -if inCCA then -if self:_InQueue(self.Qmarshal,playerData.group)then -local text=string.format("negative, you are already in the Marshal queue. New marshal request denied!") -self:MessageToPlayer(playerData,text,"MARSHAL") -elseif self:_InQueue(self.Qpattern,playerData.group)then -local text=string.format("negative, you are already in the Pattern queue. Marshal request denied!") -self:MessageToPlayer(playerData,text,"MARSHAL") -elseif self:_InQueue(self.Qwaiting,playerData.group)then -local text=string.format("negative, you are in the Waiting queue with %d flights ahead of you. Marshal request denied!",#self.Qwaiting) -self:MessageToPlayer(playerData,text,"MARSHAL") -elseif not _unit:InAir()then -local text=string.format("negative, you are not airborne. Marshal request denied!") -self:MessageToPlayer(playerData,text,"MARSHAL") -elseif playerData.name~=playerData.seclead then -local text=string.format("negative, your section lead %s needs to request Marshal.",playerData.seclead) -self:MessageToPlayer(playerData,text,"MARSHAL") -else -local freestack=self:_GetFreeStack(playerData.ai) -if freestack then -self:_MarshalPlayer(playerData,freestack) -else -self:_WaitPlayer(playerData) -end -end -else -local text=string.format("negative, you are not inside CCA. Marshal request denied!") -self:MessageToPlayer(playerData,text,"MARSHAL") -end -end -end -end -function AIRBOSS:_RequestEmergency(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text="" -if not self.emergency then -text="negative, no emergency landings on my carrier. We are currently busy. See how you get along!" -elseif not _unit:InAir()then -local zone=self:_GetZoneCarrierBox() -if playerData.unit:IsInZone(zone)then -text="roger, you are now technically in the bolter pattern. Your next step after takeoff is abeam!" -local lead=self:_GetFlightLead(playerData) -self:_SetPlayerStep(lead,AIRBOSS.PatternStep.BOLTER) -for _,sec in pairs(lead.section)do -local sectionmember=sec -self:_SetPlayerStep(sectionmember,AIRBOSS.PatternStep.BOLTER) -end -self:_RemoveFlightFromQueue(self.Qwaiting,lead) -if self:_InQueue(self.Qmarshal,lead.group)then -self:_RemoveFlightFromMarshalQueue(lead) -else -if not self:_InQueue(self.Qpattern,lead.group)then -self:_AddFlightToPatternQueue(lead) -end -end -else -text=string.format("negative, you are not airborne. Request denied!") -end -else -text="affirmative, you can bypass the pattern and are cleared for final approach!" -local lead=self:_GetFlightLead(playerData) -self:_SetPlayerStep(lead,AIRBOSS.PatternStep.EMERGENCY) -for _,sec in pairs(lead.section)do -local sectionmember=sec -self:_SetPlayerStep(sectionmember,AIRBOSS.PatternStep.EMERGENCY) -self:_RemoveFlightFromQueue(self.Qspinning,sectionmember) -end -self:_RemoveFlightFromQueue(self.Qwaiting,lead) -if self:_InQueue(self.Qmarshal,lead.group)then -self:_RemoveFlightFromMarshalQueue(lead) -else -if not self:_InQueue(self.Qpattern,lead.group)then -self:_AddFlightToPatternQueue(lead) -end -end -end -self:MessageToPlayer(playerData,text,"AIRBOSS") -end -end -end -function AIRBOSS:_RequestSpinning(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text="" -if not self:_InQueue(self.Qpattern,playerData.group)then -text="negative, you have to be in the pattern to spin it!" -elseif playerData.step==AIRBOSS.PatternStep.SPINNING then -text="negative, you are already spinning." -elseif not(playerData.step==AIRBOSS.PatternStep.BREAKENTRY or -playerData.step==AIRBOSS.PatternStep.EARLYBREAK or -playerData.step==AIRBOSS.PatternStep.LATEBREAK)then -text="negative, you have to be in the right step to spin it!" -else -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.SPINNING) -table.insert(self.Qspinning,playerData) -local call=self:_NewRadioCall(self.LSOCall.SPINIT,"AIRBOSS","Spin it!",self.Tmessage,playerData.onboard) -self:RadioTransmission(self.LSORadio,call,nil,nil,nil,true) -if playerData.difficulty==AIRBOSS.Difficulty.EASY then -local text="Climb to 1200 feet and proceed to the initial again." -self:MessageToPlayer(playerData,text,"INSTRUCTOR","") -end -return -end -self:MessageToPlayer(playerData,text,"AIRBOSS") -end -end -end -function AIRBOSS:_RequestCommence(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text="" -local cleared=false -if _unit:IsInZone(self.zoneCCA)then -local stack=playerData.flag -local _,npattern=self:_GetQueueInfo(self.Qpattern) -if self:_InQueue(self.Qpattern,playerData.group)then -text=string.format("negative, %s, you are already in the Pattern queue.",playerData.name) -elseif not _unit:InAir()then -text=string.format("negative, %s, you are not airborne.",playerData.name) -elseif playerData.seclead~=playerData.name then -text=string.format("negative, %s, your section leader %s has to request commence!",playerData.name,playerData.seclead) -elseif stack>1 then -text=string.format("negative, %s, it's not your turn yet! You are in stack no. %s.",playerData.name,stack) -elseif npattern>=self.Nmaxpattern then -text=string.format("negative ghostrider, pattern is full!\nThere are %d aircraft currently in the pattern.",npattern) -elseif self:IsRecovering()==false and not self.airbossnice then -if self.recoverywindow then -local clock=UTILS.SecondsToClock(self.recoverywindow.START) -text=string.format("negative, carrier is currently not recovery. Next window will open at %s.",clock) -else -text=string.format("negative, carrier is not recovering. No future windows planned.") -end -elseif not self:_InQueue(self.Qmarshal,playerData.group)and not self.airbossnice then -text="negative, you have to request Marshal before you can commence." -else -text=text.."roger." -if not self:IsRecovering()then -text=text.." Carrier is not recovering currently! However, you are cleared anyway as I have a nice day." -end -if not self:_InQueue(self.Qmarshal,playerData.group)then -playerData.case=self.case -if self.TACANon and playerData.difficulty~=AIRBOSS.Difficulty.HARD then -local radial=self:GetRadial(playerData.case,true,true,true) -if playerData.case==1 then -radial=self:GetBRC() -end -text=text..string.format("\nSelect TACAN %03d°, Channel %d%s (%s).\n",radial,self.TACANchannel,self.TACANmode,self.TACANmorse) -end -for _,flight in pairs(playerData.section)do -flight.case=playerData.case -end -self:_AddFlightToPatternQueue(playerData) -end -cleared=true -end -else -text=string.format("negative, %s, you are not inside the CCA!",playerData.name) -end -self:T(self.lid..text) -self:MessageToPlayer(playerData,text,"MARSHAL") -if cleared then -self:_Commencing(playerData,false) -end -end -end -end -function AIRBOSS:_RequestRefueling(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text -if self.tanker then -if _unit:IsInZone(self.zoneCCA)then -if self.tanker:IsRunning()or self.tanker:IsRefueling()then -local angels=self:_GetAngels(self.tanker.altitude) -text=string.format("affirmative, proceed to tanker at angels %d.",angels) -if self.tanker.TACANon then -text=text..string.format("\nTanker TACAN channel %d%s (%s).",self.tanker.TACANchannel,self.tanker.TACANmode,self.tanker.TACANmorse) -text=text..string.format("\nRadio frequency %.3f MHz AM.",self.tanker.RadioFreq) -end -if self.tanker:IsRefueling()then -text=text.."\nTanker is currently refueling. You might have to queue up." -end -self:_RemoveFlightFromMarshalQueue(playerData,true) -self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.REFUELING) -for _,sec in pairs(playerData.section)do -local sectext="follow your section leader to the tanker." -self:MessageToPlayer(sec,sectext,"MARSHAL") -self:_SetPlayerStep(sec,AIRBOSS.PatternStep.REFUELING) -end -elseif self.tanker:IsReturning()then -text="negative, tanker is RTB. Request denied!\nWait for the tanker to be back on station if you can." -end -else -text="negative, you are not inside the CCA yet." -end -else -text="negative, no refueling tanker available." -end -self:MessageToPlayer(playerData,text,"MARSHAL") -end -end -end -function AIRBOSS:_RemoveSectionMember(playerData,sectionmember) -for i,_flight in pairs(playerData.section)do -local flight=_flight -if flight.name==sectionmember.name then -table.remove(playerData.section,i) -return true -end -end -return false -end -function AIRBOSS:_SetSection(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local mycoord=_unit:GetCoordinate() -local dmax=100 -local text -if self.NmaxSection==0 then -text=string.format("negative, setting sections is disabled in this mission. You stay alone.") -elseif self:_InQueue(self.Qmarshal,playerData.group)then -text=string.format("negative, you are already in the Marshal queue. Setting section not possible any more!") -elseif self:_InQueue(self.Qpattern,playerData.group)then -text=string.format("negative, you are already in the Pattern queue. Setting section not possible any more!") -else -if playerData.seclead~=playerData.name then -local lead=self.players[playerData.seclead] -if lead then -local removed=self:_RemoveSectionMember(lead,playerData) -if removed then -self:MessageToPlayer(lead,string.format("Flight %s has been removed from your section.",playerData.name),"AIRBOSS","",5) -self:MessageToPlayer(playerData,string.format("You have been removed from %s's section.",lead.name),"AIRBOSS","",5) -end -end -end -local section={} -for _,_flight in pairs(self.flights)do -local flight=_flight -if flight.ai==false and flight.groupname~=playerData.groupname and#flight.section==0 and flight.seclead==flight.name then -local distance=flight.group:GetCoordinate():Get3DDistance(mycoord) -if distance0 then -_playerResults[playerName]=Paverage/n -end -end -end -local text=string.format("Greenie Board (top ten):") -local i=1 -for _playerName,_points in UTILS.spairs(_playerResults,function(t,a,b)return t[b]=0 then -text=text..string.format("(%.1f)",grade.points) -end -end -i=i+1 -if i>10 then -break -end -end -if i==1 then -text=text.."\nNo results yet." -end -local playerData=self.players[_playername] -if playerData.client then -MESSAGE:New(text,30,nil,true):ToClient(playerData.client) -end -end -end -function AIRBOSS:_DisplayPlayerGrades(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text=string.format("Your last 10 grades, %s:",_playername) -local playerGrades=self.playerscores[_playername]or{} -local p=0 -local n=0 -local m=0 -for i=#playerGrades,1,-1 do -local grade=playerGrades[i] -if grade.points>=0 then -local points=grade.finalscore or grade.points -if m<10 then -text=text..string.format("\n[%d] %s %.1f PT - %s",i,grade.grade,points,grade.details) -if grade.wire and grade.wire<=4 then -text=text..string.format(" %d-wire",grade.wire) -end -if grade.Tgroove and grade.Tgroove<=360 then -text=text..string.format(" Tgroove=%.1f s",grade.Tgroove) -end -end -if grade.finalscore then -p=p+grade.finalscore -n=n+1 -end -m=m+1 -end -end -if n>0 then -text=text..string.format("\nAverage points = %.1f",p/n) -else -text=text..string.format("\nNo data available.") -end -if playerData.client then -MESSAGE:New(text,30,nil,true):ToClient(playerData.client) -end -end -end -end -function AIRBOSS:_DisplayDebriefing(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local text=string.format("Debriefing:") -if#playerData.lastdebrief>0 then -text=text..string.format("\n================================\n") -for _,_data in pairs(playerData.lastdebrief)do -local step=_data.step -local comment=_data.hint -text=text..string.format("* %s:",step) -text=text..string.format("%s\n",comment) -end -else -text=text.." Nothing to show yet." -end -self:MessageToPlayer(playerData,text,nil,"",30,true) -end -end -end -function AIRBOSS:_DisplayQueue(_unitname,qname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -local queue=nil -if qname=="Marshal"then -queue=self.Qmarshal -elseif qname=="Pattern"then -queue=self.Qpattern -elseif qname=="Waiting"then -queue=self.Qwaiting -end -local Nqueue,nqueue=self:_GetQueueInfo(queue,playerData.case) -local text=string.format("%s Queue:",qname) -if#queue==0 then -text=text.." empty" -else -local N=0 -if qname=="Marshal"then -for i,_flight in pairs(queue)do -local flight=_flight -local charlie=self:_GetCharlieTime(flight) -local Charlie=UTILS.SecondsToClock(charlie) -local stack=flight.flag -local angels=self:_GetAngels(self:_GetMarshalAltitude(stack,flight.case)) -local _,nunit,nsec=self:_GetFlightUnits(flight,true) -local nick=self:_GetACNickname(flight.actype) -N=N+nunit -text=text..string.format("\n[Stack %d] %s (%s*%d+%d): Case %d, Angels %d, Charlie %s",stack,flight.onboard,nick,nunit,nsec,flight.case,angels,tostring(Charlie)) -end -elseif qname=="Pattern"or qname=="Waiting"then -for i,_flight in pairs(queue)do -local flight=_flight -local _,nunit,nsec=self:_GetFlightUnits(flight,true) -local nick=self:_GetACNickname(flight.actype) -local ptime=UTILS.SecondsToClock(timer.getAbsTime()-flight.time) -N=N+nunit -text=text..string.format("\n[%d] %s (%s*%d+%d): Case %d, T=%s",i,flight.onboard,nick,nunit,nsec,flight.case,ptime) -end -end -text=text..string.format("\nTotal AC: %d (airborne %d)",N,nqueue) -end -self:MessageToPlayer(playerData,text,nil,"",nil,true) -end -end -end -function AIRBOSS:_DisplayCarrierInfo(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -local coord=self:GetCoordinate() -local carrierheading=self.carrier:GetHeading() -local carrierspeed=UTILS.MpsToKnots(self.carrier:GetVelocityMPS()) -local tacan="unknown" -local icls="unknown" -if self.TACANon and self.TACANchannel~=nil then -tacan=string.format("%d%s (%s)",self.TACANchannel,self.TACANmode,self.TACANmorse) -end -if self.ICLSon and self.ICLSchannel~=nil then -icls=string.format("%d (%s)",self.ICLSchannel,self.ICLSmorse) -end -local wind=UTILS.MpsToKnots(select(1,self:GetWindOnDeck())) -local Nmarshal,nmarshal=self:_GetQueueInfo(self.Qmarshal,playerData.case) -local Npattern,npattern=self:_GetQueueInfo(self.Qpattern) -local Nspinning,nspinning=self:_GetQueueInfo(self.Qspinning) -local Nwaiting,nwaiting=self:_GetQueueInfo(self.Qwaiting) -local Ntotal,ntotal=self:_GetQueueInfo(self.flights) -local Tabs=timer.getAbsTime() -local recoverytext="Recovery time windows (max 5):" -if#self.recoverytimes==0 then -recoverytext=recoverytext.." none." -else -local rw=0 -for _,_recovery in pairs(self.recoverytimes)do -local recovery=_recovery -if Tabs=5 then -break -end -end -end -end -local tankertext=nil -if self.tanker then -tankertext=string.format("Recovery tanker frequency %.3f MHz\n",self.tanker.RadioFreq) -if self.tanker.TACANon then -tankertext=tankertext..string.format("Recovery tanker TACAN %d%s (%s)",self.tanker.TACANchannel,self.tanker.TACANmode,self.tanker.TACANmorse) -else -tankertext=tankertext.."Recovery tanker TACAN n/a" -end -end -local state=self:GetState() -if state=="Idle"then -state="Deck closed" -end -if self.turning then -state=state.." (turning currently)" -end -local text=string.format("%s info:\n",self.alias) -text=text..string.format("================================\n") -text=text..string.format("Carrier state: %s\n",state) -if self.case==1 then -text=text..string.format("Case %d recovery ops\n",self.case) -else -local radial=self:GetRadial(self.case,true,true,false) -text=text..string.format("Case %d recovery ops\nMarshal radial %03d°\n",self.case,radial) -end -text=text..string.format("BRC %03d° - FB %03d°\n",self:GetBRC(),self:GetFinalBearing(true)) -text=text..string.format("Speed %.1f kts - Wind on deck %.1f kts\n",carrierspeed,wind) -text=text..string.format("Tower frequency %.3f MHz\n",self.TowerFreq) -text=text..string.format("Marshal radio %.3f MHz\n",self.MarshalFreq) -text=text..string.format("LSO radio %.3f MHz\n",self.LSOFreq) -text=text..string.format("TACAN Channel %s\n",tacan) -text=text..string.format("ICLS Channel %s\n",icls) -if tankertext then -text=text..tankertext.."\n" -end -text=text..string.format("# A/C total %d (%d)\n",Ntotal,ntotal) -text=text..string.format("# A/C marshal %d (%d)\n",Nmarshal,nmarshal) -text=text..string.format("# A/C pattern %d (%d) - spinning %d (%d)\n",Npattern,npattern,Nspinning,nspinning) -text=text..string.format("# A/C waiting %d (%d)\n",Nwaiting,nwaiting) -text=text..string.format(recoverytext) -self:T2(self.lid..text) -self:MessageToPlayer(playerData,text,nil,"",30,true) -else -self:E(self.lid..string.format("ERROR: Could not get player data for player %s.",playername)) -end -end -end -function AIRBOSS:_DisplayCarrierWeather(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local text="" -local coord=self:GetCoordinate() -local T=coord:GetTemperature() -local P=coord:GetPressure() -local Wd,Ws=self:GetWind(nil,true) -local Bn,Bd=UTILS.BeaufortScale(Ws) -local WodPA,WodPP=self:GetWindOnDeck() -local WodPA=UTILS.MpsToKnots(WodPA) -local WodPP=UTILS.MpsToKnots(WodPP) -local WD=string.format('%03d°',Wd) -local Ts=string.format("%d°C",T) -local tT=string.format("%d°C",T) -local tW=string.format("%.1f knots",UTILS.MpsToKnots(Ws)) -local tP=string.format("%.2f inHg",UTILS.hPa2inHg(P)) -text=text..string.format("Weather Report at Carrier %s:\n",self.alias) -text=text..string.format("================================\n") -text=text..string.format("Temperature %s\n",tT) -text=text..string.format("Wind from %s at %s (%s)\n",WD,tW,Bd) -text=text..string.format("Wind on deck || %.1f kts, == %.1f kts\n",WodPA,WodPP) -text=text..string.format("QFE %.1f hPa = %s",P,tP) -if self.staticweather then -local clouds,visibility,fog,dust=self:_GetStaticWeather() -text=text..string.format("\nVisibility %.1f NM",UTILS.MetersToNM(visibility)) -text=text..string.format("\nCloud base %d ft",UTILS.MetersToFeet(clouds.base)) -text=text..string.format("\nCloud thickness %d ft",UTILS.MetersToFeet(clouds.thickness)) -text=text..string.format("\nCloud density %d",clouds.density) -text=text..string.format("\nPrecipitation %d",clouds.iprecptns) -if fog then -text=text..string.format("\nFog thickness %d ft",UTILS.MetersToFeet(fog.thickness)) -text=text..string.format("\nFog visibility %d ft",UTILS.MetersToFeet(fog.visibility)) -else -text=text..string.format("\nNo fog") -end -if dust then -text=text..string.format("\nDust density %d",dust) -else -text=text..string.format("\nNo dust") -end -end -self:T2(self.lid..text) -self:MessageToPlayer(self.players[playername],text,nil,"",30,true) -else -self:E(self.lid..string.format("ERROR! Could not find player unit in CarrierWeather! Unit name = %s",_unitname)) -end -end -function AIRBOSS:_SetDifficulty(_unitname,difficulty) -self:T2({difficulty=difficulty,unitname=_unitname}) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -playerData.difficulty=difficulty -local text=string.format("roger, your skill level is now: %s.",difficulty) -self:MessageToPlayer(playerData,text,nil,playerData.name,5) -else -self:E(self.lid..string.format("ERROR: Could not get player data for player %s.",playername)) -end -if playerData.difficulty==AIRBOSS.Difficulty.HARD then -playerData.showhints=false -else -playerData.showhints=true -end -end -end -function AIRBOSS:_SetHintsOnOff(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -playerData.showhints=not playerData.showhints -local text="" -if playerData.showhints==true then -text=string.format("roger, hints are now ON.") -else -text=string.format("affirm, hints are now OFF.") -end -self:MessageToPlayer(playerData,text,nil,playerData.name,5) -end -end -end -function AIRBOSS:_DisplayAttitude(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -playerData.attitudemonitor=not playerData.attitudemonitor -end -end -end -function AIRBOSS:_SubtitlesOnOff(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -playerData.subtitles=not playerData.subtitles -local text="" -if playerData.subtitles==true then -text=string.format("roger, subtitiles are now ON.") -elseif playerData.subtitles==false then -text=string.format("affirm, subtitiles are now OFF.") -end -self:MessageToPlayer(playerData,text,nil,playerData.name,5) -end -end -end -function AIRBOSS:_TrapsheetOnOff(_unitname) -self:F2(_unitname) -local unit,playername=self:_GetPlayerUnitAndName(_unitname) -if unit and playername then -local playerData=self.players[playername] -if playerData then -local text="" -if self.trapsheet then -playerData.trapon=not playerData.trapon -if playerData.trapon==true then -text=string.format("roger, your trapsheets are now SAVED.") -else -text=string.format("affirm, your trapsheets are NOT SAVED.") -end -else -text="negative, trap sheet data recorder is broken on this carrier." -end -self:MessageToPlayer(playerData,text,nil,playerData.name,5) -end -end -end -function AIRBOSS:_DisplayPlayerStatus(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local steptext=playerData.step -if playerData.step==AIRBOSS.PatternStep.HOLDING then -if playerData.holding==nil then -steptext="Transit to Marshal" -elseif playerData.holding==false then -steptext="Marshal (outside zone)" -elseif playerData.holding==true then -steptext="Marshal Stack Holding" -end -end -local stack=playerData.flag -local stacktext=nil -if stack>0 then -local stackalt=self:_GetMarshalAltitude(stack) -local angels=self:_GetAngels(stackalt) -stacktext=string.format("Marshal Stack %d, Angels %d\n",stack,angels) -if playerData.step==AIRBOSS.PatternStep.HOLDING and playerData.case>1 then -local radial=self:GetRadial(playerData.case,true,true,true) -stacktext=stacktext..string.format("Select TACAN %03d°, %d DME\n",radial,angels+15) -end -end -local fuel=playerData.unit:GetFuel()*100 -local fuelstate=self:_GetFuelState(playerData.unit) -local _,nunitsGround=self:_GetFlightUnits(playerData,true) -local _,nunitsAirborne=self:_GetFlightUnits(playerData,false) -local text=string.format("Status of player %s (%s)\n",playerData.name,playerData.callsign) -text=text..string.format("================================\n") -text=text..string.format("Step: %s\n",steptext) -if stacktext then -text=text..stacktext -end -text=text..string.format("Recovery Case: %d\n",playerData.case) -text=text..string.format("Skill Level: %s\n",playerData.difficulty) -text=text..string.format("Modex: %s (%s)\n",playerData.onboard,self:_GetACNickname(playerData.actype)) -text=text..string.format("Fuel State: %.1f lbs/1000 (%.1f %%)\n",fuelstate/1000,fuel) -text=text..string.format("# units: %d (%d airborne)\n",nunitsGround,nunitsAirborne) -text=text..string.format("Section Lead: %s (%d/%d)",tostring(playerData.seclead),#playerData.section+1,self.NmaxSection+1) -for _,_sec in pairs(playerData.section)do -local sec=_sec -text=text..string.format("\n- %s",sec.name) -end -if playerData.step==AIRBOSS.PatternStep.INITIAL then -local zoneinitial=self:GetCoordinate():Translate(UTILS.NMToMeters(3.5),self:GetRadial(2,false,false,false)) -local flyhdg=playerData.unit:GetCoordinate():HeadingTo(zoneinitial) -local flydist=UTILS.MetersToNM(playerData.unit:GetCoordinate():Get2DDistance(zoneinitial)) -local brc=self:GetBRC() -text=text..string.format("\nTo Initial: Fly heading %03d° for %.1f NM and turn to BRC %03d°",flyhdg,flydist,brc) -elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then -local zoneplatform=self:_GetZonePlatform(playerData.case):GetCoordinate() -local flyhdg=playerData.unit:GetCoordinate():HeadingTo(zoneplatform) -local flydist=UTILS.MetersToNM(playerData.unit:GetCoordinate():Get2DDistance(zoneplatform)) -local hdg=self:GetRadial(playerData.case,true,true,true) -text=text..string.format("\nTo Platform: Fly heading %03d° for %.1f NM and turn to %03d°",flyhdg,flydist,hdg) -end -self:MessageToPlayer(playerData,text,nil,"",30,true) -else -self:E(self.lid..string.format("ERROR: playerData=nil. Unit name=%s, player name=%s",_unitName,_playername)) -end -else -self:E(self.lid..string.format("ERROR: could not find player for unit %s",_unitName)) -end -end -function AIRBOSS:_MarkMarshalZone(_unitName,flare) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local stack=playerData.flag -local case=playerData.case -local text="" -if stack>0 then -local zoneHolding=self:_GetZoneHolding(case,stack) -local zoneThree=self:_GetZoneCommence(case,stack) -local patternalt=self:_GetMarshalAltitude(stack,case) -patternalt=5 -text="roger, marking" -if flare then -text=text..string.format("\n* Marshal zone stack %d with WHITE flares.",stack) -zoneHolding:FlareZone(FLARECOLOR.White,45,nil,patternalt) -text=text.."\n* Commence zone with RED flares." -zoneThree:FlareZone(FLARECOLOR.Red,45,nil,patternalt) -else -text=text..string.format("\n* Marshal zone stack %d with WHITE smoke.",stack) -zoneHolding:SmokeZone(SMOKECOLOR.White,45,patternalt) -text=text.."\n* Commence zone with RED smoke." -zoneThree:SmokeZone(SMOKECOLOR.Red,45,patternalt) -end -else -text="negative, you are currently not in a Marshal stack. No zones will be marked!" -end -self:MessageToPlayer(playerData,text,"MARSHAL",playerData.name) -end -end -end -function AIRBOSS:_MarkCaseZones(_unitName,flare) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -local case=playerData.case -local text=string.format("affirm, marking CASE %d zones",case) -if flare then -if case==1 or case==2 then -text=text.."\n* initial with GREEN flares" -self:_GetZoneInitial(case):FlareZone(FLARECOLOR.Green,45) -end -if case==2 or case==3 then -text=text.."\n* approach corridor with GREEN flares" -self:_GetZoneCorridor(case):FlareZone(FLARECOLOR.Green,45) -end -if case==2 or case==3 then -text=text.."\n* platform with RED flares" -self:_GetZonePlatform(case):FlareZone(FLARECOLOR.Red,45) -end -if case==3 then -text=text.."\n* dirty up with YELLOW flares" -self:_GetZoneDirtyUp(case):FlareZone(FLARECOLOR.Yellow,45) -end -if case==2 or case==3 then -if math.abs(self.holdingoffset)>0 then -self:_GetZoneArcIn(case):FlareZone(FLARECOLOR.White,45) -text=text.."\n* arc turn in with WHITE flares" -self:_GetZoneArcOut(case):FlareZone(FLARECOLOR.White,45) -text=text.."\n* arc trun out with WHITE flares" -end -end -if case==3 then -text=text.."\n* bullseye with GREEN flares" -self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.Green,45) -end -if self.carriertype==AIRBOSS.CarrierType.TARAWA then -text=text.."\n* abeam landing stop with RED flares" -local ALSPT=self:_GetZoneAbeamLandingSpot() -ALSPT:FlareZone(FLARECOLOR.Red,5,nil,UTILS.FeetToMeters(110)) -text=text.."\n* primary landing spot with GREEN flares" -local LSPT=self:_GetZoneLandingSpot() -LSPT:FlareZone(FLARECOLOR.Green,5,nil,self.carrierparam.deckheight) -end -else -if case==1 or case==2 then -text=text.."\n* initial with GREEN smoke" -self:_GetZoneInitial(case):SmokeZone(SMOKECOLOR.Green,45) -end -if case==2 or case==3 then -text=text.."\n* approach corridor with GREEN smoke" -self:_GetZoneCorridor(case):SmokeZone(SMOKECOLOR.Green,45) -end -if case==2 or case==3 then -text=text.."\n* platform with RED smoke" -self:_GetZonePlatform(case):SmokeZone(SMOKECOLOR.Red,45) -end -if case==2 or case==3 then -if math.abs(self.holdingoffset)>0 then -self:_GetZoneArcIn(case):SmokeZone(SMOKECOLOR.Blue,45) -text=text.."\n* arc turn in with BLUE smoke" -self:_GetZoneArcOut(case):SmokeZone(SMOKECOLOR.Blue,45) -text=text.."\n* arc trun out with BLUE smoke" -end -end -if case==3 then -text=text.."\n* dirty up with ORANGE smoke" -self:_GetZoneDirtyUp(case):SmokeZone(SMOKECOLOR.Orange,45) -end -if case==3 then -text=text.."\n* bullseye with GREEN smoke" -self:_GetZoneBullseye(case):SmokeZone(SMOKECOLOR.Green,45) -end -end -self:MessageToPlayer(playerData,text,"MARSHAL",playerData.name) -end -end -end -function AIRBOSS:_LSORadioCheck(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -self:RadioTransmission(self.LSORadio,self.LSOCall.RADIOCHECK,nil,nil,nil,true) -end -end -end -function AIRBOSS:_MarshalRadioCheck(_unitName) -self:F(_unitName) -local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) -if _unit and _playername then -local playerData=self.players[_playername] -if playerData then -self:RadioTransmission(self.MarshalRadio,self.MarshalCall.RADIOCHECK,nil,nil,nil,true) -end -end -end -function AIRBOSS:_SaveTrapSheet(playerData,grade) -if playerData.trapsheet==nil or#playerData.trapsheet==0 or not io then -return -end -local function _savefile(filename,data) -local f=io.open(filename,"wb") -if f then -f:write(data) -f:close() -else -self:E(self.lid..string.format("ERROR: could not save trap sheet to file %s.\nFile may contain invalid characters.",tostring(filename))) -end -end -local path=self.trappath -if lfs then -path=path or lfs.writedir() -end -local filename=nil -for i=1,9999 do -if self.trapprefix then -filename=string.format("%s_%s-%04d.csv",self.trapprefix,playerData.actype,i) -else -local name=UTILS.ReplaceIllegalCharacters(playerData.name,"_") -filename=string.format("AIRBOSS-%s_Trapsheet-%s_%s-%04d.csv",self.alias,name,playerData.actype,i) -end -if path~=nil then -filename=path.."\\"..filename -end -local _exists=UTILS.FileExists(filename) -if not _exists then -break -end -end -local text=string.format("Saving player %s trapsheet to file %s",playerData.name,filename) -self:I(self.lid..text) -local data="#Time,Rho,X,Z,Alt,AoA,GSE,LUE,Vtot,Vy,Gamma,Pitch,Roll,Yaw,Step,Grade,Points,Details\n" -local g0=playerData.trapsheet[1] -local T0=g0.Time -for i=1,#playerData.trapsheet do -local groove=playerData.trapsheet[i] -local t=groove.Time-T0 -local a=UTILS.MetersToNM(groove.Rho or 0) -local b=-groove.X or 0 -local c=groove.Z or 0 -local d=UTILS.MetersToFeet(groove.Alt or 0) -local e=groove.AoA or 0 -local f=groove.GSE or 0 -local g=-groove.LUE or 0 -local h=UTILS.MpsToKnots(groove.Vel or 0) -local i=(groove.Vy or 0)*196.85 -local j=groove.Gamma or 0 -local k=groove.Pitch or 0 -local l=groove.Roll or 0 -local m=groove.Yaw or 0 -local n=self:_GS(groove.Step,-1)or"n/a" -local o=groove.Grade or"n/a" -local p=groove.GradePoints or 0 -local q=groove.GradeDetail or"n/a" -data=data..string.format("%.2f,%.3f,%.1f,%.1f,%.1f,%.2f,%.2f,%.2f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%s,%s,%.1f,%s\n",t,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) -end -_savefile(filename,data) -end -function AIRBOSS:onbeforeSave(From,Event,To,path,filename) -if not io then -self:E(self.lid.."ERROR: io not desanitized. Can't save player grades.") -return false -end -if path==nil and not lfs then -self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.") -end -return true -end -function AIRBOSS:onafterSave(From,Event,To,path,filename) -local function _savefile(filename,data) -local f=assert(io.open(filename,"wb")) -f:write(data) -f:close() -end -if lfs then -path=path or lfs.writedir() -end -filename=filename or string.format("AIRBOSS-%s_LSOgrades.csv",self.alias) -if path~=nil then -filename=path.."\\"..filename -end -local scores="Name,Pass,Points Final,Points Pass,Grade,Details,Wire,Tgroove,Case,Wind,Modex,Airframe,Carrier Type,Carrier Name,Theatre,Mission Time,Mission Date,OS Date\n" -local n=0 -for playername,grades in pairs(self.playerscores)do -for i,_grade in pairs(grades)do -local grade=_grade -local wire="n/a" -if grade.wire and grade.wire<=4 then -wire=tostring(grade.wire) -end -local Tgroove="n/a" -if grade.Tgroove and grade.Tgroove<=360 and grade.case<3 then -Tgroove=tostring(UTILS.Round(grade.Tgroove,1)) -end -local finalscore="n/a" -if grade.finalscore then -finalscore=tostring(UTILS.Round(grade.finalscore,1)) -end -scores=scores..string.format("%s,%d,%s,%.1f,%s,%s,%s,%s,%d,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", -playername,i,finalscore,grade.points,grade.grade,grade.details,wire,Tgroove,grade.case, -grade.wind,grade.modex,grade.airframe,grade.carriertype,grade.carriername,grade.theatre,grade.mitime,grade.midate,grade.osdate) -n=n+1 -end -end -local text=string.format("Saving %d player LSO grades to file %s",n,filename) -self:I(self.lid..text) -_savefile(filename,scores) -end -function AIRBOSS:onbeforeLoad(From,Event,To,path,filename) -local function _fileexists(name) -local f=io.open(name,"r") -if f~=nil then -io.close(f) -return true -else -return false -end -end -if not io then -self:E(self.lid.."WARNING: io not desanitized. Can't load player grades.") -return false -end -if path==nil and not lfs then -self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.") -end -if lfs then -path=path or lfs.writedir() -end -filename=filename or string.format("AIRBOSS-%s_LSOgrades.csv",self.alias) -if path~=nil then -filename=path.."\\"..filename -end -local exists=_fileexists(filename) -if exists then -return true -else -self:E(self.lid..string.format("WARNING: Player LSO grades file %s does not exist.",filename)) -return false -end -end -function AIRBOSS:onafterLoad(From,Event,To,path,filename) -local function _loadfile(filename) -local f=assert(io.open(filename,"rb")) -local data=f:read("*all") -f:close() -return data -end -if lfs then -path=path or lfs.writedir() -end -filename=filename or string.format("AIRBOSS-%s_LSOgrades.csv",self.alias) -if path~=nil then -filename=path.."\\"..filename -end -local text=string.format("Loading player LSO grades from file %s",filename) -MESSAGE:New(text,10):ToAllIf(self.Debug) -self:I(self.lid..text) -local data=_loadfile(filename) -local playergrades=UTILS.Split(data,"\n") -table.remove(playergrades,1) -self.playerscores={} -local n=0 -for _,gradeline in pairs(playergrades)do -local gradedata=UTILS.Split(gradeline,",") -self:T2(gradedata) -local grade={} -local playername=gradedata[1] -if gradedata[3]~=nil and gradedata[3]~="n/a"then -grade.finalscore=tonumber(gradedata[3]) -end -grade.points=tonumber(gradedata[4]) -grade.grade=tostring(gradedata[5]) -grade.details=tostring(gradedata[6]) -if gradedata[7]~=nil and gradedata[7]~="n/a"then -grade.wire=tonumber(gradedata[7]) -end -if gradedata[8]~=nil and gradedata[8]~="n/a"then -grade.Tgroove=tonumber(gradedata[8]) -end -grade.case=tonumber(gradedata[9]) -grade.wind=gradedata[10]or"n/a" -grade.modex=gradedata[11]or"n/a" -grade.airframe=gradedata[12]or"n/a" -grade.carriertype=gradedata[13]or"n/a" -grade.carriername=gradedata[14]or"n/a" -grade.theatre=gradedata[15]or"n/a" -grade.mitime=gradedata[16]or"n/a" -grade.midate=gradedata[17]or"n/a" -grade.osdate=gradedata[18]or"n/a" -self.playerscores[playername]=self.playerscores[playername]or{} -table.insert(self.playerscores[playername],grade) -n=n+1 -self:T2({playername,self.playerscores[playername]}) -end -local text=string.format("Loaded %d player LSO grades from file %s",n,filename) -self:I(self.lid..text) -end -RECOVERYTANKER={ -ClassName="RECOVERYTANKER", -Debug=false, -lid=nil, -carrier=nil, -carriertype=nil, -tankergroupname=nil, -tanker=nil, -airbase=nil, -beacon=nil, -TACANchannel=nil, -TACANmode=nil, -TACANmorse=nil, -TACANon=nil, -RadioFreq=nil, -RadioModu=nil, -altitude=nil, -speed=nil, -distStern=nil, -distBow=nil, -dTupdate=nil, -Dupdate=nil, -Hupdate=nil, -Tupdate=nil, -takeoff=nil, -lowfuel=nil, -respawn=nil, -respawninair=nil, -uncontrolledac=nil, -orientation=nil, -orientlast=nil, -position=nil, -alias=nil, -uid=0, -awacs=nil, -callsignname=nil, -callsignnumber=nil, -modex=nil, -eplrs=nil, -recovery=nil, -terminaltype=nil, -} -_RECOVERYTANKERID=0 -RECOVERYTANKER.version="1.0.9" -function RECOVERYTANKER:New(carrierunit,tankergroupname) -local self=BASE:Inherit(self,FSM:New()) -if type(carrierunit)=="string"then -self.carrier=UNIT:FindByName(carrierunit) -else -self.carrier=carrierunit -end -self.carriertype=self.carrier:GetTypeName() -self.tankergroupname=tankergroupname -_RECOVERYTANKERID=_RECOVERYTANKERID+1 -self.uid=_RECOVERYTANKERID -self.carrier:SetState(self.carrier,string.format("RECOVERYTANKER_%d",self.uid),self) -self.alias=string.format("%s_%s_%02d",self.carrier:GetName(),self.tankergroupname,_RECOVERYTANKERID) -self.lid=string.format("RECOVERYTANKER %s | ",self.alias) -self:SetAltitude() -self:SetSpeed() -self:SetRacetrackDistances() -self:SetHomeBase(AIRBASE:FindByName(self.carrier:GetName())) -self:SetTakeoffHot() -self:SetLowFuelThreshold() -self:SetRespawnOnOff() -self:SetTACAN() -self:SetRadio() -self:SetPatternUpdateDistance() -self:SetPatternUpdateHeading() -self:SetPatternUpdateInterval() -self:SetAWACS(false) -self:SetRecoveryAirboss(false) -self.terminaltype=AIRBASE.TerminalType.OpenMedOrBig -if false then -BASE:TraceOnOff(true) -BASE:TraceClass(self.ClassName) -BASE:TraceLevel(1) -end -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Running") -self:AddTransition("*","RefuelStart","Refueling") -self:AddTransition("*","RefuelStop","Running") -self:AddTransition("*","Run","Running") -self:AddTransition("Running","RTB","Returning") -self:AddTransition("Returning","Returned","Returned") -self:AddTransition("*","Status","*") -self:AddTransition("Running","PatternUpdate","*") -self:AddTransition("*","Stop","Stopped") -return self -end -function RECOVERYTANKER:SetSpeed(speed) -self.speed=UTILS.KnotsToMps(speed or 274) -return self -end -function RECOVERYTANKER:SetAltitude(altitude) -self.altitude=UTILS.FeetToMeters(altitude or 6000) -return self -end -function RECOVERYTANKER:SetRacetrackDistances(distbow,diststern) -self.distBow=UTILS.NMToMeters(distbow or 10) -self.distStern=-UTILS.NMToMeters(diststern or 4) -return self -end -function RECOVERYTANKER:SetPatternUpdateInterval(interval) -self.dTupdate=(interval or 10)*60 -return self -end -function RECOVERYTANKER:SetPatternUpdateDistance(distancechange) -self.Dupdate=UTILS.NMToMeters(distancechange or 5) -return self -end -function RECOVERYTANKER:SetPatternUpdateHeading(headingchange) -self.Hupdate=headingchange or 5 -return self -end -function RECOVERYTANKER:SetLowFuelThreshold(fuelthreshold) -self.lowfuel=fuelthreshold or 10 -return self -end -function RECOVERYTANKER:SetHomeBase(airbase,terminaltype) -if type(airbase)=="string"then -self.airbase=AIRBASE:FindByName(airbase) -else -self.airbase=airbase -end -if not self.airbase then -self:E(self.lid.."ERROR: Airbase is nil!") -end -if terminaltype then -self.terminaltype=terminaltype -end -return self -end -function RECOVERYTANKER:SetRecoveryAirboss(switch) -if switch==true or switch==nil then -self.recovery=true -else -self.recovery=false -end -return self -end -function RECOVERYTANKER:SetAWACS(switch,eplrs) -if switch==nil or switch==true then -self.awacs=true -else -self.awacs=false -end -if eplrs==nil or eplrs==true then -self.eplrs=true -else -self.eplrs=false -end -return self -end -function RECOVERYTANKER:SetCallsign(callsignname,callsignnumber) -self.callsignname=callsignname -self.callsignnumber=callsignnumber -return self -end -function RECOVERYTANKER:SetModex(modex) -self.modex=modex -return self -end -function RECOVERYTANKER:SetTakeoff(takeofftype) -self.takeoff=takeofftype -return self -end -function RECOVERYTANKER:SetTakeoffHot() -self:SetTakeoff(SPAWN.Takeoff.Hot) -return self -end -function RECOVERYTANKER:SetTakeoffCold() -self:SetTakeoff(SPAWN.Takeoff.Cold) -return self -end -function RECOVERYTANKER:SetTakeoffAir() -self:SetTakeoff(SPAWN.Takeoff.Air) -return self -end -function RECOVERYTANKER:SetRespawnOn() -self.respawn=true -return self -end -function RECOVERYTANKER:SetRespawnOff() -self.respawn=false -return self -end -function RECOVERYTANKER:SetRespawnOnOff(switch) -if switch==nil or switch==true then -self.respawn=true -else -self.respawn=false -end -return self -end -function RECOVERYTANKER:SetRespawnInAir() -self.respawninair=true -return self -end -function RECOVERYTANKER:SetUseUncontrolledAircraft() -self.uncontrolledac=true -return self -end -function RECOVERYTANKER:SetTACANoff() -self.TACANon=false -return self -end -function RECOVERYTANKER:SetTACAN(channel,morse) -self.TACANchannel=channel or 1 -self.TACANmode="Y" -self.TACANmorse=morse or"TKR" -self.TACANon=true -return self -end -function RECOVERYTANKER:SetRadio(frequency,modulation) -self.RadioFreq=frequency or 251 -self.RadioModu=modulation or"AM" -return self -end -function RECOVERYTANKER:SetDebugModeON() -self.Debug=true -return self -end -function RECOVERYTANKER:SetDebugModeOFF() -self.Debug=false -return self -end -function RECOVERYTANKER:IsReturning() -return self:is("Returning") -end -function RECOVERYTANKER:IsReturned() -return self:is("Returned") -end -function RECOVERYTANKER:IsRunning() -return self:is("Running") -end -function RECOVERYTANKER:IsRefueling() -return self:is("Refueling") -end -function RECOVERYTANKER:IsStopped() -return self:is("Stopped") -end -function RECOVERYTANKER:GetAlias() -return self.alias -end -function RECOVERYTANKER:GetUnitName() -local unit=self.tanker:GetUnit(1) -if unit then -return unit:GetName() -end -return nil -end -function RECOVERYTANKER:onafterStart(From,Event,To) -self:I(string.format("Starting Recovery Tanker v%s for carrier unit %s of type %s for tanker group %s.",RECOVERYTANKER.version,self.carrier:GetName(),self.carriertype,self.tankergroupname)) -self:HandleEvent(EVENTS.EngineShutdown) -self:HandleEvent(EVENTS.Land) -self:HandleEvent(EVENTS.Refueling,self._RefuelingStart) -self:HandleEvent(EVENTS.RefuelingStop,self._RefuelingStop) -self:HandleEvent(EVENTS.Crash,self._OnEventCrashOrDead) -self:HandleEvent(EVENTS.Dead,self._OnEventCrashOrDead) -local Spawn=SPAWN:NewWithAlias(self.tankergroupname,self.alias) -Spawn:InitRadioCommsOnOff(true) -Spawn:InitRadioFrequency(self.RadioFreq) -Spawn:InitRadioModulation(self.RadioModu) -Spawn:InitModex(self.modex) -if self.takeoff==SPAWN.Takeoff.Air then -local hdg=self.carrier:GetHeading() -local dist=-self.distStern+UTILS.NMToMeters(4) -local Carrier=self.carrier:GetCoordinate():Translate(dist,hdg+190):SetAltitude(self.altitude) -Spawn:InitHeading(hdg+10) -self.tanker=Spawn:SpawnFromCoordinate(Carrier) -else -if self.uncontrolledac then -self.tanker=GROUP:FindByName(self.tankergroupname) -if self.tanker:IsAlive()then -self.tanker:StartUncontrolled() -else -self:E(string.format("ERROR: No uncontrolled (alive) tanker group with name %s could be found!",self.tankergroupname)) -return -end -else -self.tanker=Spawn:SpawnAtAirbase(self.airbase,self.takeoff,nil,self.terminaltype) -end -end -self:ScheduleOnce(1,self._InitRoute,self,-self.distStern+UTILS.NMToMeters(3)) -if self.TACANon then -self:_ActivateTACAN(2) -end -if self.callsignname then -self.tanker:CommandSetCallsign(self.callsignname,self.callsignnumber,2) -end -if self.eplrs then -self.tanker:CommandEPLRS(true,3) -end -self.orientation=self.carrier:GetOrientationX() -self.orientlast=self.carrier:GetOrientationX() -self.position=self.carrier:GetCoordinate() -self:__Status(10) -end -function RECOVERYTANKER:onafterStatus(From,Event,To) -local time=timer.getTime() -if self.tanker and self.tanker:IsAlive()then -local fuel=self.tanker:GetFuel()*100 -local life=self.tanker:GetUnit(1):GetLife() -local life0=self.tanker:GetUnit(1):GetLife0() -local lifeR=self.tanker:GetUnit(1):GetLifeRelative() -local text=string.format("Recovery tanker %s: state=%s fuel=%.1f, life=%.1f/%.1f=%d",self.tanker:GetName(),self:GetState(),fuel,life,life0,lifeR*100) -self:T(self.lid..text) -MESSAGE:New(text,10):ToAllIf(self.Debug) -if self:IsRunning()then -if fuel100 then -return -end -local text=string.format("Recovery tanker %s started refueling unit %s",self.tanker:GetName(),receiver:GetName()) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:T(self.lid..text) -self:RefuelStart(receiver) -end -end -function RECOVERYTANKER:_RefuelingStop(EventData) -if EventData and EventData.IniUnit and EventData.IniUnit:IsAlive()then -local receiver=EventData.IniUnit -local dist=receiver:GetCoordinate():Get2DDistance(self.tanker:GetCoordinate()) -if dist>100 then -return -end -local text=string.format("Recovery tanker %s stopped refueling unit %s",self.tanker:GetName(),receiver:GetName()) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:T(self.lid..text) -self:RefuelStop(receiver) -end -end -function RECOVERYTANKER:_OnEventCrashOrDead(EventData) -self:F2({eventdata=EventData}) -if EventData and EventData.IniUnit then -local unit=EventData.IniUnit -local unitname=tostring(EventData.IniUnitName) -if EventData.IniGroupName==self.tanker:GetName()then -self:E(self.lid..string.format("Recovery tanker %s crashed!",unitname)) -self:Stop() -if self.respawn then -self:__Start(5) -end -end -end -end -function RECOVERYTANKER:_InitPatternTaskFunction() -local carriername=self.carrier:GetName() -local DCSScript={} -DCSScript[#DCSScript+1]=string.format('local mycarrier = UNIT:FindByName(\"%s\") ',carriername) -DCSScript[#DCSScript+1]=string.format('local mytanker = mycarrier:GetState(mycarrier, \"RECOVERYTANKER_%d\") ',self.uid) -DCSScript[#DCSScript+1]=string.format('mytanker:PatternUpdate()') -local DCSTask=CONTROLLABLE.TaskWrappedAction(self,CONTROLLABLE.CommandDoScript(self,table.concat(DCSScript))) -return DCSTask -end -function RECOVERYTANKER:_InitRoute(dist,delay) -dist=dist or UTILS.NMToMeters(8) -delay=delay or 1 -self:T(self.lid..string.format("Initializing route of recovery tanker %s.",self.tanker:GetName())) -local Carrier=self.carrier:GetCoordinate() -local hdg=self.carrier:GetHeading() -local p=Carrier:Translate(dist,hdg+190):SetAltitude(self.altitude) -local speed=self.tanker:GetSpeedMax()*0.8 -if self.Debug then -p:MarkToAll(string.format("Enter Pattern WP: alt=%d ft, speed=%d kts",UTILS.MetersToFeet(self.altitude),speed*0.539957)) -end -local task=self:_InitPatternTaskFunction() -local wp={} -if self.takeoff==SPAWN.Takeoff.Air then -wp[#wp+1]=self.tanker:GetCoordinate():SetAltitude(self.altitude):WaypointAirTurningPoint(nil,speed,{},"Spawn Position") -else -wp[#wp+1]=Carrier:WaypointAirTakeOffParking() -end -wp[#wp+1]=p:WaypointAirTurningPoint(nil,speed,{task},"Enter Pattern") -self.tanker:Route(wp,delay) -self:__Run(1) -self.Tupdate=nil -end -function RECOVERYTANKER:_CheckPatternUpdate(dt) -local pos=self.carrier:GetCoordinate() -local vNew=self.carrier:GetOrientationX() -local vOld=self.orientation -local vLast=self.orientlast -vNew.y=0;vOld.y=0;vLast.y=0 -local deltaHeading=math.deg(math.acos(UTILS.VecDot(vNew,vOld)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vOld))) -local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast))) -self.orientlast=vNew -local turning=deltaLast>=1 -if turning then -self:T2(self.lid..string.format("Carrier is turning. Delta Heading = %.1f",deltaLast)) -end -local Hchange=false -if math.abs(deltaHeading)>=self.Hupdate then -self:T(self.lid..string.format("Carrier heading changed by %d degrees. Turning=%s.",deltaHeading,tostring(turning))) -Hchange=true -end -local dist=pos:Get2DDistance(self.position) -local Dchange=false -if dist>self.Dupdate then -self:T(self.lid..string.format("Carrier position changed by %.1f NM. Turning=%s.",UTILS.MetersToNM(dist),tostring(turning))) -Dchange=true -end -local update=false -if self:IsRunning()and dt>self.dTupdate and not turning then -if Hchange or Dchange then -local text=string.format("Updating tanker %s pattern due to carrier position=%s or heading=%s change.",self.tanker:GetName(),tostring(Dchange),tostring(Hchange)) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:T(self.lid..text) -self.orientation=vNew -self.position=pos -update=true -end -end -return update -end -function RECOVERYTANKER:_ActivateTACAN(delay) -if delay and delay>0 then -self:ScheduleOnce(delay,RECOVERYTANKER._ActivateTACAN,self) -else -local unit=self.tanker:GetUnit(1) -if unit and unit:IsAlive()then -local text=string.format("Activating TACAN beacon: channel=%d mode=%s, morse=%s.",self.TACANchannel,self.TACANmode,self.TACANmorse) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:T(self.lid..text) -self.beacon=BEACON:New(unit) -self.beacon:ActivateTACAN(self.TACANchannel,self.TACANmode,self.TACANmorse,true) -else -self:E(self.lid.."ERROR: Recovery tanker is not alive!") -end -end -end -function RECOVERYTANKER:_Pattern() -local hdg=self.carrier:GetHeading() -local alt=self.altitude -local Carrier=self.carrier:GetCoordinate() -local width=UTILS.NMToMeters(8) -local p={} -p[1]=self.tanker:GetCoordinate() -p[2]=Carrier:SetAltitude(alt) -p[3]=p[2]:Translate(self.distBow,hdg) -p[4]=p[3]:Translate(width/math.sqrt(2),hdg-45) -p[5]=p[3]:Translate(width,hdg-90) -p[6]=p[5]:Translate(self.distStern-self.distBow,hdg) -p[7]=p[2]:Translate(self.distStern,hdg) -local wp={} -for i=1,#p do -local coord=p[i] -coord:MarkToAll(string.format("Waypoint %d",i)) -table.insert(wp,coord:WaypointAirTurningPoint(nil,UTILS.MpsToKmph(self.speed))) -end -return wp -end -RESCUEHELO={ -ClassName="RESCUEHELO", -Debug=false, -lid=nil, -carrier=nil, -carriertype=nil, -helogroupname=nil, -helo=nil, -airbase=nil, -takeoff=nil, -followset=nil, -formation=nil, -lowfuel=nil, -altitude=nil, -offsetX=nil, -offsetZ=nil, -rescuezone=nil, -respawn=nil, -respawninair=nil, -uncontrolledac=nil, -rescueon=nil, -rescueduration=nil, -rescuespeed=nil, -rescuestopboat=nil, -HeloFuel0=nil, -rtb=nil, -carrierstop=nil, -alias=nil, -uid=0, -modex=nil, -dtFollow=nil, -} -_RESCUEHELOID=0 -RESCUEHELO.version="1.1.0" -function RESCUEHELO:New(carrierunit,helogroupname) -local self=BASE:Inherit(self,FSM:New()) -if type(carrierunit)=="string"then -self.carrier=UNIT:FindByName(carrierunit) -else -self.carrier=carrierunit -end -self.carriertype=self.carrier:GetTypeName() -self.helogroupname=helogroupname -_RESCUEHELOID=_RESCUEHELOID+1 -self.uid=_RESCUEHELOID -self.carrier:SetState(self.carrier,string.format("RESCUEHELO_%d",self.uid),self) -self.alias=string.format("%s_%s_%02d",self.carrier:GetName(),self.helogroupname,_RESCUEHELOID) -self.lid=string.format("RESCUEHELO %s | ",self.alias) -self:SetHomeBase(AIRBASE:FindByName(self.carrier:GetName())) -self:SetTakeoffHot() -self:SetLowFuelThreshold() -self:SetAltitude() -self:SetOffsetX() -self:SetOffsetZ() -self:SetRespawnOn() -self:SetRescueOn() -self:SetRescueZone() -self:SetRescueHoverSpeed() -self:SetRescueDuration() -self:SetFollowTimeInterval() -self:SetRescueStopBoatOff() -self.rtb=false -self.carrierstop=false -if false then -self.Debug=true -BASE:TraceOnOff(true) -BASE:TraceClass(self.ClassName) -BASE:TraceLevel(1) -end -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Running") -self:AddTransition("Running","Rescue","Rescuing") -self:AddTransition("Running","RTB","Returning") -self:AddTransition("Rescuing","RTB","Returning") -self:AddTransition("Returning","Returned","Returned") -self:AddTransition("Running","Run","Running") -self:AddTransition("Returned","Run","Running") -self:AddTransition("*","Status","*") -self:AddTransition("*","Stop","Stopped") -return self -end -function RESCUEHELO:SetLowFuelThreshold(threshold) -self.lowfuel=threshold or 5 -return self -end -function RESCUEHELO:SetHomeBase(airbase) -if type(airbase)=="string"then -self.airbase=AIRBASE:FindByName(airbase) -else -self.airbase=airbase -end -if not self.airbase then -self:E(self.lid.."ERROR: Airbase is nil!") -end -return self -end -function RESCUEHELO:SetRescueZone(radius) -radius=UTILS.NMToMeters(radius or 15) -self.rescuezone=ZONE_UNIT:New("Rescue Zone",self.carrier,radius) -return self -end -function RESCUEHELO:SetRescueHoverSpeed(speed) -self.rescuespeed=UTILS.KnotsToMps(speed or 5) -return self -end -function RESCUEHELO:SetRescueDuration(duration) -self.rescueduration=(duration or 5)*60 -return self -end -function RESCUEHELO:SetRescueOn() -self.rescueon=true -return self -end -function RESCUEHELO:SetRescueOff() -self.rescueon=false -return self -end -function RESCUEHELO:SetRescueStopBoatOn() -self.rescuestopboat=true -return self -end -function RESCUEHELO:SetRescueStopBoatOff() -self.rescuestopboat=false -return self -end -function RESCUEHELO:SetTakeoff(takeofftype) -self.takeoff=takeofftype or SPAWN.Takeoff.Hot -return self -end -function RESCUEHELO:SetTakeoffHot() -self:SetTakeoff(SPAWN.Takeoff.Hot) -return self -end -function RESCUEHELO:SetTakeoffCold() -self:SetTakeoff(SPAWN.Takeoff.Cold) -return self -end -function RESCUEHELO:SetTakeoffAir() -self:SetTakeoff(SPAWN.Takeoff.Air) -return self -end -function RESCUEHELO:SetAltitude(alt) -self.altitude=alt or 70 -return self -end -function RESCUEHELO:SetOffsetX(distance) -self.offsetX=distance or 200 -return self -end -function RESCUEHELO:SetOffsetZ(distance) -self.offsetZ=distance or 240 -return self -end -function RESCUEHELO:SetRespawnOn() -self.respawn=true -return self -end -function RESCUEHELO:SetRespawnOff() -self.respawn=false -return self -end -function RESCUEHELO:SetRespawnOnOff(switch) -if switch==nil or switch==true then -self.respawn=true -else -self.respawn=false -end -return self -end -function RESCUEHELO:SetRespawnInAir() -self.respawninair=true -return self -end -function RESCUEHELO:SetModex(modex) -self.modex=modex -return self -end -function RESCUEHELO:SetFollowTimeInterval(dt) -self.dtFollow=dt or 1.0 -return self -end -function RESCUEHELO:SetUseUncontrolledAircraft() -self.uncontrolledac=true -return self -end -function RESCUEHELO:SetDebugModeON() -self.Debug=true -return self -end -function RESCUEHELO:SetDebugModeOFF() -self.Debug=false -return self -end -function RESCUEHELO:IsReturning() -return self:is("Returning") -end -function RESCUEHELO:IsRunning() -return self:is("Running") -end -function RESCUEHELO:IsRescuing() -return self:is("Rescuing") -end -function RESCUEHELO:IsStopped() -return self:is("Stopped") -end -function RESCUEHELO:GetAlias() -return self.alias -end -function RESCUEHELO:GetUnitName() -local unit=self.helo:GetUnit(1) -if unit then -return unit:GetName() -end -return nil -end -function RESCUEHELO:OnEventLand(EventData) -local group=EventData.IniGroup -if group and group:IsAlive()then -local groupname=group:GetName() -if groupname==self.helo:GetName()then -local airbase=nil -local airbasename="unknown" -if EventData.Place then -airbase=EventData.Place -airbasename=airbase:GetName() -end -local text=string.format("Rescue helo group %s landed at airbase %s.",groupname,airbasename) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:T(self.lid..text) -if self:IsRescuing()then -self:T(self.lid..string.format("Rescue helo %s returned from rescue operation.",groupname)) -end -if self.takeoff==SPAWN.Takeoff.Air or self.respawninair then -if not self:IsRescuing()then -self:E(self.lid..string.format("WARNING: Rescue helo %s landed. This should not happen for Takeoff=Air or respawninair=true and no rescue operation in progress.",groupname)) -end -end -self:__Returned(3,airbase) -end -end -end -function RESCUEHELO:_OnEventCrashOrEject(EventData) -self:F2({eventdata=EventData}) -if EventData and EventData.IniUnit then -local unit=EventData.IniUnit -local unitname=tostring(EventData.IniUnitName) -if EventData.IniGroupName~=self.helo:GetName()then -local text=string.format("Unit %s crashed or ejected.",unitname) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:I(self.lid..text) -local coord=unit:GetCoordinate() -if coord and self.rescuezone:IsCoordinateInZone(coord)then -if self.Debug then -coord:MarkToCoalition(self.lid..string.format("Crash site of unit %s.",unitname),self.helo:GetCoalition()) -end -local rightcoalition=EventData.IniGroup:GetCoalition()==self.helo:GetCoalition() -if self:IsRunning()and self.rescueon and rightcoalition then -self:Rescue(coord) -end -end -else -self:E(self.lid..string.format("Rescue helo %s crashed!",unitname)) -self:Stop() -if self.respawn then -self:__Start(5) -end -end -end -end -function RESCUEHELO:onafterStart(From,Event,To) -local text=string.format("Starting Rescue Helo Formation v%s for carrier unit %s of type %s.",RESCUEHELO.version,self.carrier:GetName(),self.carriertype) -self:I(self.lid..text) -self:HandleEvent(EVENTS.Land) -self:HandleEvent(EVENTS.Crash,self._OnEventCrashOrEject) -self:HandleEvent(EVENTS.Ejection,self._OnEventCrashOrEject) -local delay=120 -local Spawn=SPAWN:NewWithAlias(self.helogroupname,self.alias) -Spawn:InitModex(self.modex) -if self.takeoff==SPAWN.Takeoff.Air then -local hdg=self.carrier:GetHeading() -local dist=UTILS.NMToMeters(0.2) -local Carrier=self.carrier:GetCoordinate():Translate(dist,hdg):SetAltitude(math.max(100,self.altitude)) -Spawn:InitHeading(hdg) -self.helo=Spawn:SpawnFromCoordinate(Carrier) -delay=1 -else -if self.uncontrolledac then -self.helo=GROUP:FindByName(self.helogroupname) -if self.helo and self.helo:IsAlive()then -self.helo:StartUncontrolled() -delay=60 -else -self:E(string.format("ERROR: No uncontrolled (alive) rescue helo group with name %s could be found!",self.helogroupname)) -return -end -else -self.helo=Spawn:SpawnAtAirbase(self.airbase,self.takeoff,nil,AIRBASE.TerminalType.HelicopterUsable) -if self.takeoff==SPAWN.Takeoff.Runway then -delay=5 -elseif self.takeoff==SPAWN.Takeoff.Hot then -delay=30 -elseif self.takeoff==SPAWN.Takeoff.Cold then -delay=60 -end -end -end -self.followset=SET_GROUP:New() -self.followset:AddGroup(self.helo) -self.HeloFuel0=self.helo:GetFuel() -self.formation=AI_FORMATION:New(self.carrier,self.followset,"Helo Formation with Carrier","Follow Carrier at given parameters.") -self.formation:FormationCenterWing(-self.offsetX,50,math.abs(self.altitude),50,self.offsetZ,50) -self.formation:SetFollowTimeInterval(self.dtFollow) -self.formation:SetFlightModeFormation(self.helo) -self.formation:__Start(delay) -self:__Status(1) -end -function RESCUEHELO:onafterStatus(From,Event,To) -local time=timer.getTime() -if self.helo and self.helo:IsAlive()then -local fuel=self.helo:GetFuel()*100 -local fuelrel=fuel/self.HeloFuel0 -local life=self.helo:GetUnit(1):GetLife() -local life0=self.helo:GetUnit(1):GetLife0() -local lifeR=self.helo:GetUnit(1):GetLifeRelative() -local text=string.format("Rescue Helo %s: state=%s fuel=%.1f, rel.fuel=%.1f, life=%.1f/%.1f=%d",self.helo:GetName(),self:GetState(),fuel,fuelrel,life,life0,lifeR*100) -MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug) -self:T(self.lid..text) -if self:IsRunning()then -if fuelUTILS.FeetToMeters(1500)then -dust=nil -end -local visibilitymin=visibility -if fog then -if fog.visibility10 then -reportedviz=10 -end -VISIBILITY=string.format("%d",reportedviz) -else -local reportedviz=UTILS.Round(UTILS.MetersToSM(visibilitymin)) -if reportedviz>10 then -reportedviz=10 -end -VISIBILITY=string.format("%d",reportedviz) -end -local cloudbase=clouds.base -local cloudceil=clouds.base+clouds.thickness -local clouddens=clouds.density -local cloudspreset=clouds.preset or"Nothing" -local precepitation=0 -if cloudspreset:find("Preset10")then -clouddens=4 -elseif cloudspreset:find("Preset11")then -clouddens=4 -elseif cloudspreset:find("Preset12")then -clouddens=4 -elseif cloudspreset:find("Preset13")then -clouddens=7 -elseif cloudspreset:find("Preset14")then -clouddens=7 -elseif cloudspreset:find("Preset15")then -clouddens=7 -elseif cloudspreset:find("Preset16")then -clouddens=7 -elseif cloudspreset:find("Preset17")then -clouddens=7 -elseif cloudspreset:find("Preset18")then -clouddens=7 -elseif cloudspreset:find("Preset19")then -clouddens=7 -elseif cloudspreset:find("Preset20")then -clouddens=7 -elseif cloudspreset:find("Preset21")then -clouddens=9 -elseif cloudspreset:find("Preset22")then -clouddens=9 -elseif cloudspreset:find("Preset23")then -clouddens=9 -elseif cloudspreset:find("Preset24")then -clouddens=9 -elseif cloudspreset:find("Preset25")then -clouddens=9 -elseif cloudspreset:find("Preset26")then -clouddens=9 -elseif cloudspreset:find("Preset27")then -clouddens=9 -elseif cloudspreset:find("Preset1")then -clouddens=1 -elseif cloudspreset:find("Preset2")then -clouddens=1 -elseif cloudspreset:find("Preset3")then -clouddens=4 -elseif cloudspreset:find("Preset4")then -clouddens=4 -elseif cloudspreset:find("Preset5")then -clouddens=4 -elseif cloudspreset:find("Preset6")then -clouddens=4 -elseif cloudspreset:find("Preset7")then -clouddens=4 -elseif cloudspreset:find("Preset8")then -clouddens=4 -elseif cloudspreset:find("Preset9")then -clouddens=4 -elseif cloudspreset:find("RainyPreset")then -clouddens=9 -if temperature>5 then -precepitation=1 -else -precepitation=3 -end -end -local CLOUDBASE=string.format("%d",UTILS.MetersToFeet(cloudbase)) -local CLOUDCEIL=string.format("%d",UTILS.MetersToFeet(cloudceil)) -if self.metric then -CLOUDBASE=string.format("%d",cloudbase) -CLOUDCEIL=string.format("%d",cloudceil) -end -local CLOUDBASE1000,CLOUDBASE0100=self:_GetThousandsAndHundreds(UTILS.MetersToFeet(cloudbase)) -local CLOUDCEIL1000,CLOUDCEIL0100=self:_GetThousandsAndHundreds(UTILS.MetersToFeet(cloudceil)) -if self.metric then -CLOUDBASE1000,CLOUDBASE0100=self:_GetThousandsAndHundreds(cloudbase) -CLOUDCEIL1000,CLOUDCEIL0100=self:_GetThousandsAndHundreds(cloudceil) -end -local CloudCover={} -CloudCover=ATIS.Sound.CloudsNotAvailable -local CLOUDSsub="Cloud coverage information not available" -if static then -if clouddens>=9 then -CloudCover=ATIS.Sound.CloudsOvercast -CLOUDSsub="Overcast" -elseif clouddens>=7 then -CloudCover=ATIS.Sound.CloudsBroken -CLOUDSsub="Broken clouds" -elseif clouddens>=4 then -CloudCover=ATIS.Sound.CloudsScattered -CLOUDSsub="Scattered clouds" -elseif clouddens>=1 then -CloudCover=ATIS.Sound.CloudsFew -CLOUDSsub="Few clouds" -else -CLOUDBASE=nil -CLOUDCEIL=nil -CloudCover=ATIS.Sound.CloudsNo -CLOUDSsub="No clouds" -end -end -local subtitle="" -subtitle=string.format("%s",self.airbasename) -if self.airbasename:find("AFB")==nil and self.airbasename:find("Airport")==nil and self.airbasename:find("Airstrip")==nil and self.airbasename:find("airfield")==nil and self.airbasename:find("AB")==nil then -subtitle=subtitle.." Airport" -end -self.radioqueue:NewTransmission(string.format("%s/%s.ogg",self.theatre,self.airbasename),3.0,self.soundpath,nil,nil,subtitle,self.subduration) -local alltext=subtitle -subtitle=string.format("Information %s",NATO) -local _INFORMATION=subtitle -self:Transmission(ATIS.Sound.Information,0.5,subtitle) -self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg",NATO),0.75,self.soundpath) -alltext=alltext..";\n"..subtitle -subtitle=string.format("%s Zulu",ZULU) -self.radioqueue:Number2Transmission(ZULU,nil,0.5) -self:Transmission(ATIS.Sound.Zulu,0.2,subtitle) -alltext=alltext..";\n"..subtitle -if not self.zulutimeonly then -subtitle=string.format("Sunrise at %s local time",SUNRISE) -self:Transmission(ATIS.Sound.SunriseAt,0.5,subtitle) -self.radioqueue:Number2Transmission(SUNRISE,nil,0.2) -self:Transmission(ATIS.Sound.TimeLocal,0.2) -alltext=alltext..";\n"..subtitle -subtitle=string.format("Sunset at %s local time",SUNSET) -self:Transmission(ATIS.Sound.SunsetAt,0.5,subtitle) -self.radioqueue:Number2Transmission(SUNSET,nil,0.5) -self:Transmission(ATIS.Sound.TimeLocal,0.2) -alltext=alltext..";\n"..subtitle -end -if self.metric then -subtitle=string.format("Wind from %s at %s m/s",WINDFROM,WINDSPEED) -else -subtitle=string.format("Wind from %s at %s knots",WINDFROM,WINDSPEED) -end -if turbulence>0 then -subtitle=subtitle..", gusting" -end -local _WIND=subtitle -self:Transmission(ATIS.Sound.WindFrom,1.0,subtitle) -self.radioqueue:Number2Transmission(WINDFROM) -self:Transmission(ATIS.Sound.At,0.2) -self.radioqueue:Number2Transmission(WINDSPEED) -if self.metric then -self:Transmission(ATIS.Sound.MetersPerSecond,0.2) -else -self:Transmission(ATIS.Sound.Knots,0.2) -end -if turbulence>0 then -self:Transmission(ATIS.Sound.Gusting,0.2) -end -alltext=alltext..";\n"..subtitle -if self.metric then -subtitle=string.format("Visibility %s km",VISIBILITY) -else -subtitle=string.format("Visibility %s SM",VISIBILITY) -end -self:Transmission(ATIS.Sound.Visibilty,1.0,subtitle) -self.radioqueue:Number2Transmission(VISIBILITY) -if self.metric then -self:Transmission(ATIS.Sound.Kilometers,0.2) -else -self:Transmission(ATIS.Sound.StatuteMiles,0.2) -end -alltext=alltext..";\n"..subtitle -local wp=false -local wpsub="" -if precepitation==1 then -wp=true -wpsub=wpsub.." rain" -elseif precepitation==2 then -if wp then -wpsub=wpsub.."," -end -wpsub=wpsub.." thunderstorm" -wp=true -elseif precepitation==3 then -wpsub=wpsub.." snow" -wp=true -elseif precepitation==4 then -wpsub=wpsub.." snowstorm" -wp=true -end -if fog then -if wp then -wpsub=wpsub.."," -end -wpsub=wpsub.." fog" -wp=true -end -if dust then -if wp then -wpsub=wpsub.."," -end -wpsub=wpsub.." dust" -wp=true -end -if wp then -subtitle=string.format("Weather phenomena:%s",wpsub) -self:Transmission(ATIS.Sound.WeatherPhenomena,1.0,subtitle) -if precepitation==1 then -self:Transmission(ATIS.Sound.Rain,0.5) -elseif precepitation==2 then -self:Transmission(ATIS.Sound.ThunderStorm,0.5) -elseif precepitation==3 then -self:Transmission(ATIS.Sound.Snow,0.5) -elseif precepitation==4 then -self:Transmission(ATIS.Sound.SnowStorm,0.5) -end -if fog then -self:Transmission(ATIS.Sound.Fog,0.5) -end -if dust then -self:Transmission(ATIS.Sound.Dust,0.5) -end -alltext=alltext..";\n"..subtitle -end -self:Transmission(CloudCover,1.0,CLOUDSsub) -if CLOUDBASE and static then -if self.metric then -subtitle=string.format("Cloudbase %s, ceiling %s meters",CLOUDBASE,CLOUDCEIL) -else -subtitle=string.format("Cloudbase %s, ceiling %s ft",CLOUDBASE,CLOUDCEIL) -end -self:Transmission(ATIS.Sound.CloudBase,1.0,subtitle) -if tonumber(CLOUDBASE1000)>0 then -self.radioqueue:Number2Transmission(CLOUDBASE1000) -self:Transmission(ATIS.Sound.Thousand,0.1) -end -if tonumber(CLOUDBASE0100)>0 then -self.radioqueue:Number2Transmission(CLOUDBASE0100) -self:Transmission(ATIS.Sound.Hundred,0.1) -end -self:Transmission(ATIS.Sound.CloudCeiling,0.5) -if tonumber(CLOUDCEIL1000)>0 then -self.radioqueue:Number2Transmission(CLOUDCEIL1000) -self:Transmission(ATIS.Sound.Thousand,0.1) -end -if tonumber(CLOUDCEIL0100)>0 then -self.radioqueue:Number2Transmission(CLOUDCEIL0100) -self:Transmission(ATIS.Sound.Hundred,0.1) -end -if self.metric then -self:Transmission(ATIS.Sound.Meters,0.1) -else -self:Transmission(ATIS.Sound.Feet,0.1) -end -end -alltext=alltext..";\n"..subtitle -if self.TDegF then -if temperature<0 then -subtitle=string.format("Temperature -%s °F",TEMPERATURE) -else -subtitle=string.format("Temperature %s °F",TEMPERATURE) -end -else -if temperature<0 then -subtitle=string.format("Temperature -%s °C",TEMPERATURE) -else -subtitle=string.format("Temperature %s °C",TEMPERATURE) -end -end -local _TEMPERATURE=subtitle -self:Transmission(ATIS.Sound.Temperature,1.0,subtitle) -if temperature<0 then -self:Transmission(ATIS.Sound.Minus,0.2) -end -self.radioqueue:Number2Transmission(TEMPERATURE) -if self.TDegF then -self:Transmission(ATIS.Sound.DegreesFahrenheit,0.2) -else -self:Transmission(ATIS.Sound.DegreesCelsius,0.2) -end -alltext=alltext..";\n"..subtitle -if self.TDegF then -if dewpoint<0 then -subtitle=string.format("Dew point -%s °F",DEWPOINT) -else -subtitle=string.format("Dew point %s °F",DEWPOINT) -end -else -if dewpoint<0 then -subtitle=string.format("Dew point -%s °C",DEWPOINT) -else -subtitle=string.format("Dew point %s °C",DEWPOINT) -end -end -local _DEWPOINT=subtitle -self:Transmission(ATIS.Sound.DewPoint,1.0,subtitle) -if dewpoint<0 then -self:Transmission(ATIS.Sound.Minus,0.2) -end -self.radioqueue:Number2Transmission(DEWPOINT) -if self.TDegF then -self:Transmission(ATIS.Sound.DegreesFahrenheit,0.2) -else -self:Transmission(ATIS.Sound.DegreesCelsius,0.2) -end -alltext=alltext..";\n"..subtitle -if self.PmmHg then -if self.qnhonly then -subtitle=string.format("Altimeter %s.%s mmHg",QNH[1],QNH[2]) -else -subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg",QNH[1],QNH[2],QFE[1],QFE[2]) -end -else -if self.metric then -if self.qnhonly then -subtitle=string.format("Altimeter %s.%s hPa",QNH[1],QNH[2]) -else -subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s hPa",QNH[1],QNH[2],QFE[1],QFE[2]) -end -else -if self.qnhonly then -subtitle=string.format("Altimeter %s.%s inHg",QNH[1],QNH[2]) -else -subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg",QNH[1],QNH[2],QFE[1],QFE[2]) -end -end -end -local _ALTIMETER=subtitle -self:Transmission(ATIS.Sound.Altimeter,1.0,subtitle) -if not self.qnhonly then -self:Transmission(ATIS.Sound.QNH,0.5) -end -self.radioqueue:Number2Transmission(QNH[1]) -if ATIS.ICAOPhraseology[UTILS.GetDCSMap()]then -self:Transmission(ATIS.Sound.Decimal,0.2) -end -self.radioqueue:Number2Transmission(QNH[2]) -if not self.qnhonly then -self:Transmission(ATIS.Sound.QFE,0.75) -self.radioqueue:Number2Transmission(QFE[1]) -if ATIS.ICAOPhraseology[UTILS.GetDCSMap()]then -self:Transmission(ATIS.Sound.Decimal,0.2) -end -self.radioqueue:Number2Transmission(QFE[2]) -end -if self.PmmHg then -self:Transmission(ATIS.Sound.MillimetersOfMercury,0.1) -else -if self.metric then -self:Transmission(ATIS.Sound.HectoPascal,0.1) -else -self:Transmission(ATIS.Sound.InchesOfMercury,0.1) -end -end -alltext=alltext..";\n"..subtitle -local subtitle=string.format("Active runway %s",runway) -if rwyLeft==true then -subtitle=subtitle.." Left" -elseif rwyLeft==false then -subtitle=subtitle.." Right" -end -local _RUNACT=subtitle -self:Transmission(ATIS.Sound.ActiveRunway,1.0,subtitle) -self.radioqueue:Number2Transmission(runway) -if rwyLeft==true then -self:Transmission(ATIS.Sound.Left,0.2) -elseif rwyLeft==false then -self:Transmission(ATIS.Sound.Right,0.2) -end -alltext=alltext..";\n"..subtitle -if self.rwylength then -local runact=self.airbase:GetActiveRunway(self.runwaym2t) -local length=runact.length -if not self.metric then -length=UTILS.MetersToFeet(length) -end -local L1000,L0100=self:_GetThousandsAndHundreds(length) -local subtitle=string.format("Runway length %d",length) -if self.metric then -subtitle=subtitle.." meters" -else -subtitle=subtitle.." feet" -end -self:Transmission(ATIS.Sound.RunwayLength,1.0,subtitle) -if tonumber(L1000)>0 then -self.radioqueue:Number2Transmission(L1000) -self:Transmission(ATIS.Sound.Thousand,0.1) -end -if tonumber(L0100)>0 then -self.radioqueue:Number2Transmission(L0100) -self:Transmission(ATIS.Sound.Hundred,0.1) -end -if self.metric then -self:Transmission(ATIS.Sound.Meters,0.1) -else -self:Transmission(ATIS.Sound.Feet,0.1) -end -alltext=alltext..";\n"..subtitle -end -if self.elevation then -local elevation=self.airbase:GetHeight() -if not self.metric then -elevation=UTILS.MetersToFeet(elevation) -end -local L1000,L0100=self:_GetThousandsAndHundreds(elevation) -local subtitle=string.format("Elevation %d",elevation) -if self.metric then -subtitle=subtitle.." meters" -else -subtitle=subtitle.." feet" -end -self:Transmission(ATIS.Sound.Elevation,1.0,subtitle) -if tonumber(L1000)>0 then -self.radioqueue:Number2Transmission(L1000) -self:Transmission(ATIS.Sound.Thousand,0.1) -end -if tonumber(L0100)>0 then -self.radioqueue:Number2Transmission(L0100) -self:Transmission(ATIS.Sound.Hundred,0.1) -end -if self.metric then -self:Transmission(ATIS.Sound.Meters,0.1) -else -self:Transmission(ATIS.Sound.Feet,0.1) -end -alltext=alltext..";\n"..subtitle -end -if self.towerfrequency then -local freqs="" -for i,freq in pairs(self.towerfrequency)do -freqs=freqs..string.format("%.3f MHz",freq) -if i<#self.towerfrequency then -freqs=freqs..", " -end -end -subtitle=string.format("Tower frequency %s",freqs) -self:Transmission(ATIS.Sound.TowerFrequency,1.0,subtitle) -for _,freq in pairs(self.towerfrequency)do -local f=string.format("%.3f",freq) -f=UTILS.Split(f,".") -self.radioqueue:Number2Transmission(f[1],nil,0.5) -if tonumber(f[2])>0 then -self:Transmission(ATIS.Sound.Decimal,0.2) -self.radioqueue:Number2Transmission(f[2]) -end -self:Transmission(ATIS.Sound.MegaHertz,0.2) -end -alltext=alltext..";\n"..subtitle -end -local ils=self:GetNavPoint(self.ils,runway,rwyLeft) -if ils then -subtitle=string.format("ILS frequency %.2f MHz",ils.frequency) -self:Transmission(ATIS.Sound.ILSFrequency,1.0,subtitle) -local f=string.format("%.2f",ils.frequency) -f=UTILS.Split(f,".") -self.radioqueue:Number2Transmission(f[1],nil,0.5) -if tonumber(f[2])>0 then -self:Transmission(ATIS.Sound.Decimal,0.2) -self.radioqueue:Number2Transmission(f[2]) -end -self:Transmission(ATIS.Sound.MegaHertz,0.2) -alltext=alltext..";\n"..subtitle -end -local ndb=self:GetNavPoint(self.ndbouter,runway,rwyLeft) -if ndb then -subtitle=string.format("Outer NDB frequency %.2f MHz",ndb.frequency) -self:Transmission(ATIS.Sound.OuterNDBFrequency,1.0,subtitle) -local f=string.format("%.2f",ndb.frequency) -f=UTILS.Split(f,".") -self.radioqueue:Number2Transmission(f[1],nil,0.5) -if tonumber(f[2])>0 then -self:Transmission(ATIS.Sound.Decimal,0.2) -self.radioqueue:Number2Transmission(f[2]) -end -self:Transmission(ATIS.Sound.MegaHertz,0.2) -alltext=alltext..";\n"..subtitle -end -local ndb=self:GetNavPoint(self.ndbinner,runway,rwyLeft) -if ndb then -subtitle=string.format("Inner NDB frequency %.2f MHz",ndb.frequency) -self:Transmission(ATIS.Sound.InnerNDBFrequency,1.0,subtitle) -local f=string.format("%.2f",ndb.frequency) -f=UTILS.Split(f,".") -self.radioqueue:Number2Transmission(f[1],nil,0.5) -if tonumber(f[2])>0 then -self:Transmission(ATIS.Sound.Decimal,0.2) -self.radioqueue:Number2Transmission(f[2]) -end -self:Transmission(ATIS.Sound.MegaHertz,0.2) -alltext=alltext..";\n"..subtitle -end -if self.vor then -subtitle=string.format("VOR frequency %.2f MHz",self.vor) -self:Transmission(ATIS.Sound.VORFrequency,1.0,subtitle) -local f=string.format("%.2f",self.vor) -f=UTILS.Split(f,".") -self.radioqueue:Number2Transmission(f[1],nil,0.5) -if tonumber(f[2])>0 then -self:Transmission(ATIS.Sound.Decimal,0.2) -self.radioqueue:Number2Transmission(f[2]) -end -self:Transmission(ATIS.Sound.MegaHertz,0.2) -alltext=alltext..";\n"..subtitle -end -if self.tacan then -subtitle=string.format("TACAN channel %dX",self.tacan) -self:Transmission(ATIS.Sound.TACANChannel,1.0,subtitle) -self.radioqueue:Number2Transmission(tostring(self.tacan),nil,0.2) -self.radioqueue:NewTransmission("NATO Alphabet/Xray.ogg",0.75,self.soundpath,nil,0.2) -alltext=alltext..";\n"..subtitle -end -if self.rsbn then -subtitle=string.format("RSBN channel %d",self.rsbn) -self:Transmission(ATIS.Sound.RSBNChannel,1.0,subtitle) -self.radioqueue:Number2Transmission(tostring(self.rsbn),nil,0.2) -alltext=alltext..";\n"..subtitle -end -local ndb=self:GetNavPoint(self.prmg,runway,rwyLeft) -if ndb then -subtitle=string.format("PRMG channel %d",ndb.frequency) -self:Transmission(ATIS.Sound.PRMGChannel,1.0,subtitle) -self.radioqueue:Number2Transmission(tostring(ndb.frequency),nil,0.5) -alltext=alltext..";\n"..subtitle -end -subtitle=string.format("Advise on initial contact, you have information %s",NATO) -self:Transmission(ATIS.Sound.AdviceOnInitial,0.5,subtitle) -self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg",NATO),0.75,self.soundpath) -alltext=alltext..";\n"..subtitle -self:Report(alltext) -if self.usemarker then -self:UpdateMarker(_INFORMATION,_RUNACT,_WIND,_ALTIMETER,_TEMPERATURE) -end -end -function ATIS:onafterReport(From,Event,To,Text) -self:T(self.lid..string.format("Report:\n%s",Text)) -end -function ATIS:UpdateMarker(information,runact,wind,altimeter,temperature) -if self.markerid then -self.airbase:GetCoordinate():RemoveMark(self.markerid) -end -local text=string.format("ATIS on %.3f %s, %s:\n",self.frequency,UTILS.GetModulationName(self.modulation),tostring(information)) -text=text..string.format("%s\n",tostring(runact)) -text=text..string.format("%s\n",tostring(wind)) -text=text..string.format("%s\n",tostring(altimeter)) -text=text..string.format("%s",tostring(temperature)) -self.markerid=self.airbase:GetCoordinate():MarkToAll(text,true) -return self.markerid -end -function ATIS:GetActiveRunway() -local coord=self.airbase:GetCoordinate() -local height=coord:GetLandHeight() -local windFrom,windSpeed=coord:GetWind(height+10) -local runact=self.airbase:GetActiveRunway(self.runwaym2t) -local runway=self:GetMagneticRunway(windFrom)or runact.idx -local rwyLeft=nil -if self.activerunway then -local runwayno=self:GetRunwayWithoutLR(self.activerunway) -if runwayno~=""then -runway=runwayno -end -rwyLeft=self:GetRunwayLR(self.activerunway) -end -return runway,rwyLeft -end -function ATIS:GetMagneticRunway(windfrom) -local diffmin=nil -local runway=nil -for _,heading in pairs(self.runwaymag)do -local hdg=self:GetRunwayWithoutLR(heading) -local diff=UTILS.HdgDiff(windfrom,tonumber(hdg)*10) -if diffmin==nil or diffself.Tstop or false then -return false -end -local startme=self:EvalConditionsAll(self.conditionStart) -if not startme then -return false -end -return true -end -function AUFTRAG:IsReadyToCancel() -local Tnow=timer.getAbsTime() -if self.Tstop and Tnow>self.Tstop then -return true -end -local failure=self:EvalConditionsAny(self.conditionFailure) -if failure then -self.failurecondition=true -return true -end -local success=self:EvalConditionsAny(self.conditionSuccess) -if success then -self.successcondition=true -return true -end -return false -end -function AUFTRAG:EvalConditionsAll(Conditions) -for _,_condition in pairs(Conditions or{})do -local condition=_condition -local istrue=condition.func(unpack(condition.arg)) -if not istrue then -return false -end -end -return true -end -function AUFTRAG:EvalConditionsAny(Conditions) -for _,_condition in pairs(Conditions or{})do -local condition=_condition -local istrue=condition.func(unpack(condition.arg)) -if istrue then -return true -end -end -return false -end -function AUFTRAG:onafterStatus(From,Event,To) -local Tnow=timer.getAbsTime() -local Ntargets=self:CountMissionTargets() -local Ntargets0=self:GetTargetInitialNumber() -local Ngroups=self:CountOpsGroups() -if self:IsNotOver()then -if self:CheckGroupsDone()then -self:Done() -elseif(self.Tstop and Tnow>self.Tstop+10)or(Ntargets0>0 and Ntargets==0)then -self:Cancel() -end -end -local fsmstate=self:GetState() -if fsmstate~=self.status then -self:E(self.lid..string.format("ERROR: FSM state %s != %s mission status!",fsmstate,self.status)) -end -if self.verbose>=1 then -local Cstart=UTILS.SecondsToClock(self.Tstart,true) -local Cstop=self.Tstop and UTILS.SecondsToClock(self.Tstop,true)or"INF" -local targetname=self:GetTargetName()or"unknown" -local airwing=self.airwing and self.airwing.alias or"N/A" -local commander=self.wingcommander and tostring(self.wingcommander.coalition)or"N/A" -self:I(self.lid..string.format("Status %s: Target=%s, T=%s-%s, assets=%d, groups=%d, targets=%d, wing=%s, commander=%s",self.status,targetname,Cstart,Cstop,#self.assets,Ngroups,Ntargets,airwing,commander)) -end -if self.verbose>=2 then -local text="Group data:" -for groupname,_groupdata in pairs(self.groupdata)do -local groupdata=_groupdata -text=text..string.format("\n- %s: status mission=%s opsgroup=%s",groupname,groupdata.status,groupdata.opsgroup and groupdata.opsgroup:GetState()or"N/A") -end -self:I(self.lid..text) -end -local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false -if self:IsOver()and ready2evaluate then -self:Evaluate() -else -self:__Status(-30) -end -if self.markerOn then -self:UpdateMarker() -end -end -function AUFTRAG:Evaluate() -local failed=false -local targetdamage=self:GetTargetDamage() -local owndamage=self.Ncasualties/self.Nelements*100 -local Ntargets=self:CountMissionTargets() -local Ntargets0=self:GetTargetInitialNumber() -local Life=self:GetTargetLife() -local Life0=self:GetTargetInitialLife() -if Ntargets0>0 then -if self.type==AUFTRAG.Type.TROOPTRANSPORT or self.type==AUFTRAG.Type.ESCORT then -if Ntargets0 then -failed=true -end -end -else -if self.Nelements==self.Ncasualties then -failed=true -end -end -local successCondition=self:EvalConditionsAny(self.conditionSuccess) -local failureCondition=self:EvalConditionsAny(self.conditionFailure) -if failureCondition then -failed=true -elseif successCondition then -failed=false -end -local text=string.format("Evaluating mission:\n") -text=text..string.format("Own casualties = %d/%d\n",self.Ncasualties,self.Nelements) -text=text..string.format("Own losses = %.1f %%\n",owndamage) -text=text..string.format("Killed units = %d\n",self.Nkills) -text=text..string.format("--------------------------\n") -text=text..string.format("Targets left = %d/%d\n",Ntargets,Ntargets0) -text=text..string.format("Targets life = %.1f/%.1f\n",Life,Life0) -text=text..string.format("Enemy losses = %.1f %%\n",targetdamage) -text=text..string.format("--------------------------\n") -text=text..string.format("Success Cond = %s\n",tostring(successCondition)) -text=text..string.format("Failure Cond = %s\n",tostring(failureCondition)) -text=text..string.format("--------------------------\n") -text=text..string.format("Final Success = %s\n",tostring(not failed)) -text=text..string.format("=========================") -self:I(self.lid..text) -if failed then -self:Failed() -else -self:Success() -end -return self -end -function AUFTRAG:GetOpsGroups() -local opsgroups={} -for _,_groupdata in pairs(self.groupdata or{})do -local groupdata=_groupdata -table.insert(opsgroups,groupdata.opsgroup) -end -return opsgroups -end -function AUFTRAG:GetAssetDataByName(AssetName) -return self.groupdata[tostring(AssetName)] -end -function AUFTRAG:GetGroupData(opsgroup) -if opsgroup and self.groupdata then -return self.groupdata[opsgroup.groupname] -end -return nil -end -function AUFTRAG:SetGroupStatus(opsgroup,status) -self:T(self.lid..string.format("Setting flight %s to status %s",opsgroup and opsgroup.groupname or"nil",tostring(status))) -if self:GetGroupStatus(opsgroup)==AUFTRAG.GroupStatus.CANCELLED and status==AUFTRAG.GroupStatus.DONE then -else -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -groupdata.status=status -else -self:E(self.lid.."WARNING: Could not SET flight data for flight group. Setting status to DONE") -end -end -self:T2(self.lid..string.format("Setting flight %s status to %s. IsNotOver=%s CheckGroupsDone=%s",opsgroup.groupname,self:GetGroupStatus(opsgroup),tostring(self:IsNotOver()),tostring(self:CheckGroupsDone()))) -if self:IsNotOver()and self:CheckGroupsDone()then -self:T3(self.lid.."All flights done ==> mission DONE!") -self:Done() -else -self:T3(self.lid.."Mission NOT DONE yet!") -end -end -function AUFTRAG:GetGroupStatus(opsgroup) -self:T3(self.lid..string.format("Trying to get Flight status for flight group %s",opsgroup and opsgroup.groupname or"nil")) -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -return groupdata.status -else -self:E(self.lid..string.format("WARNING: Could not GET groupdata for opsgroup %s. Returning status DONE.",opsgroup and opsgroup.groupname or"nil")) -return AUFTRAG.GroupStatus.DONE -end -end -function AUFTRAG:SetGroupWaypointCoordinate(opsgroup,coordinate) -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -groupdata.waypointcoordinate=coordinate -end -end -function AUFTRAG:GetGroupWaypointCoordinate(opsgroup) -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -return groupdata.waypointcoordinate -end -end -function AUFTRAG:SetGroupWaypointTask(opsgroup,task) -self:T2(self.lid..string.format("Setting waypoint task %s",task and task.description or"WTF")) -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -groupdata.waypointtask=task -end -end -function AUFTRAG:GetGroupWaypointTask(opsgroup) -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -return groupdata.waypointtask -end -end -function AUFTRAG:SetGroupWaypointIndex(opsgroup,waypointindex) -self:T2(self.lid..string.format("Setting waypoint index %d",waypointindex)) -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -groupdata.waypointindex=waypointindex -end -end -function AUFTRAG:GetGroupWaypointIndex(opsgroup) -local groupdata=self:GetGroupData(opsgroup) -if groupdata then -return groupdata.waypointindex -end -end -function AUFTRAG:CheckGroupsDone() -if self:IsPlanned()or self:IsQueued()or self:IsRequested()then -return false -end -if self:IsStarted()and self:CountOpsGroups()==0 then -return true -end -for groupname,data in pairs(self.groupdata)do -local groupdata=data -if groupdata then -if groupdata.status==AUFTRAG.GroupStatus.DONE or groupdata.status==AUFTRAG.GroupStatus.CANCELLED then -else -return false -end -end -end -return true -end -function AUFTRAG:OnEventUnitLost(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -for _,_groupdata in pairs(self.groupdata)do -local groupdata=_groupdata -if groupdata and groupdata.opsgroup and groupdata.opsgroup.groupname==EventData.IniGroupName then -self:I(self.lid..string.format("UNIT LOST event for opsgroup %s unit %s",groupdata.opsgroup.groupname,EventData.IniUnitName)) -end -end -end -end -function AUFTRAG:onafterPlanned(From,Event,To) -self.status=AUFTRAG.Status.PLANNED -self:T(self.lid..string.format("New mission status=%s",self.status)) -end -function AUFTRAG:onafterQueued(From,Event,To,Airwing) -self.status=AUFTRAG.Status.QUEUED -self.airwing=Airwing -self:T(self.lid..string.format("New mission status=%s at airwing %s",self.status,tostring(Airwing.alias))) -end -function AUFTRAG:onafterRequested(From,Event,To) -self.status=AUFTRAG.Status.REQUESTED -self:T(self.lid..string.format("New mission status=%s",self.status)) -end -function AUFTRAG:onafterAssign(From,Event,To) -self.status=AUFTRAG.Status.ASSIGNED -self:T(self.lid..string.format("New mission status=%s",self.status)) -end -function AUFTRAG:onafterScheduled(From,Event,To) -self.status=AUFTRAG.Status.SCHEDULED -self:T(self.lid..string.format("New mission status=%s",self.status)) -end -function AUFTRAG:onafterStarted(From,Event,To) -self.status=AUFTRAG.Status.STARTED -self:T(self.lid..string.format("New mission status=%s",self.status)) -end -function AUFTRAG:onafterExecuting(From,Event,To) -self.status=AUFTRAG.Status.EXECUTING -self:T(self.lid..string.format("New mission status=%s",self.status)) -end -function AUFTRAG:onafterDone(From,Event,To) -self.status=AUFTRAG.Status.DONE -self:T(self.lid..string.format("New mission status=%s",self.status)) -self.Tover=timer.getAbsTime() -end -function AUFTRAG:onafterElementDestroyed(From,Event,To,OpsGroup,Element) -self.Ncasualties=self.Ncasualties+1 -end -function AUFTRAG:onafterGroupDead(From,Event,To,OpsGroup) -local asset=self:GetAssetByName(OpsGroup.groupname) -if asset then -self:AssetDead(asset) -end -end -function AUFTRAG:onafterAssetDead(From,Event,To,Asset) -local N=self:CountOpsGroups() -self:I(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d",tostring(Asset.spawngroupname),N)) -if N==0 then -if self:IsNotOver()then -self:Cancel() -else -end -end -self:DelAsset(Asset) -end -function AUFTRAG:onafterCancel(From,Event,To) -self:I(self.lid..string.format("CANCELLING mission in status %s. Will wait for groups to report mission DONE before evaluation",self.status)) -self.Tover=timer.getAbsTime() -self.Nrepeat=self.repeated -self.NrepeatFailure=self.repeatedFailure -self.NrepeatSuccess=self.repeatedSuccess -self.dTevaluate=0 -if self.wingcommander then -self:T(self.lid..string.format("Wingcommander will cancel the mission. Will wait for mission DONE before evaluation!")) -self.wingcommander:CancelMission(self) -elseif self.airwing then -self:T(self.lid..string.format("Airwing %s will cancel the mission. Will wait for mission DONE before evaluation!",self.airwing.alias)) -self.airwing:MissionCancel(self) -else -self:T(self.lid..string.format("No airwing or wingcommander. Attached flights will cancel the mission on their own. Will wait for mission DONE before evaluation!")) -for _,_groupdata in pairs(self.groupdata)do -local groupdata=_groupdata -groupdata.opsgroup:MissionCancel(self) -end -end -if self.status==AUFTRAG.Status.PLANNED then -self:T(self.lid..string.format("Cancelled mission was in planned stage. Call it done!")) -self:Done() -end -end -function AUFTRAG:onafterSuccess(From,Event,To) -self.status=AUFTRAG.Status.SUCCESS -self:T(self.lid..string.format("New mission status=%s",self.status)) -local repeatme=self.repeatedSuccess Repeat mission!",self.repeated+1,N)) -self:Repeat() -else -self:I(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!",self.repeated+1)) -self:Stop() -end -end -function AUFTRAG:onafterFailed(From,Event,To) -self.status=AUFTRAG.Status.FAILED -self:T(self.lid..string.format("New mission status=%s",self.status)) -local repeatme=self.repeatedFailure Repeat mission!",self.repeated+1,N)) -self:Repeat() -else -self:I(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!",self.repeated+1)) -self:Stop() -end -end -function AUFTRAG:onafterRepeat(From,Event,To) -self.status=AUFTRAG.Status.PLANNED -self:T(self.lid..string.format("New mission status=%s (on Repeat)",self.status)) -self.repeated=self.repeated+1 -if self.chief then -elseif self.wingcommander then -if self.airwing then -self.airwing:RemoveMission(self) -end -elseif self.airwing then -self:Queued(self.airwing) -else -self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, WINGCOMMANDER or AIRWING! Stopping AUFTRAG") -self:Stop() -end -self.assets={} -for _,_groupdata in pairs(self.groupdata)do -local groupdata=_groupdata -local opsgroup=groupdata.opsgroup -if opsgroup then -self:DelOpsGroup(opsgroup) -end -end -self.groupdata={} -self.Ncasualties=0 -self.Nelements=0 -self:__Status(-30) -end -function AUFTRAG:onafterStop(From,Event,To) -self:I(self.lid..string.format("STOPPED mission in status=%s. Removing missions from queues. Stopping CallScheduler!",self.status)) -if self.wingcommander then -self.wingcommander:RemoveMission(self) -end -if self.airwing then -self.airwing:RemoveMission(self) -end -for _,_groupdata in pairs(self.groupdata)do -local groupdata=_groupdata -groupdata.opsgroup:RemoveMission(self) -end -self.assets={} -self.groupdata={} -self.CallScheduler:Clear() -end -function AUFTRAG:_TargetFromObject(Object) -if not self.engageTarget then -if Object:IsInstanceOf("TARGET")then -self.engageTarget=Object -else -self.engageTarget=TARGET:New(Object) -end -else -end -return self -end -function AUFTRAG:CountMissionTargets() -if self.engageTarget then -return self.engageTarget:CountTargets() -else -return 0 -end -end -function AUFTRAG:GetTargetInitialNumber() -local target=self:GetTargetData() -if target then -return target.N0 -else -return 0 -end -end -function AUFTRAG:GetTargetInitialLife() -local target=self:GetTargetData() -if target then -return target.life0 -else -return 0 -end -end -function AUFTRAG:GetTargetDamage() -local target=self:GetTargetData() -if target then -return target:GetDamage() -else -return 0 -end -end -function AUFTRAG:GetTargetLife() -local target=self:GetTargetData() -if target then -return target:GetLife() -else -return 0 -end -end -function AUFTRAG:GetTargetData() -return self.engageTarget -end -function AUFTRAG:GetObjective() -return self:GetTargetData():GetObject() -end -function AUFTRAG:GetTargetType() -return self:GetTargetData().Type -end -function AUFTRAG:GetTargetVec2() -local coord=self:GetTargetCoordinate() -if coord then -return coord:GetVec2() -end -return nil -end -function AUFTRAG:GetTargetCoordinate() -if self.transportPickup then -return self.transportPickup -elseif self.engageTarget then -return self.engageTarget:GetCoordinate() -else -self:E(self.lid.."ERROR: Cannot get target coordinate!") -end -return nil -end -function AUFTRAG:GetTargetName() -if self.engageTarget then -return self.engageTarget:GetName() -end -return"N/A" -end -function AUFTRAG:GetTargetDistance(FromCoord) -local TargetCoord=self:GetTargetCoordinate() -if TargetCoord and FromCoord then -return TargetCoord:Get2DDistance(FromCoord) -else -self:E(self.lid.."ERROR: TargetCoord or FromCoord does not exist in AUFTRAG:GetTargetDistance() function! Returning 0") -end -return 0 -end -function AUFTRAG:AddAsset(Asset) -self.assets=self.assets or{} -table.insert(self.assets,Asset) -return self -end -function AUFTRAG:DelAsset(Asset) -for i,_asset in pairs(self.assets or{})do -local asset=_asset -if asset.uid==Asset.uid then -self:T(self.lid..string.format("Removing asset \"%s\" from mission",tostring(asset.spawngroupname))) -table.remove(self.assets,i) -return self -end -end -return self -end -function AUFTRAG:GetAssetByName(Name) -for i,_asset in pairs(self.assets or{})do -local asset=_asset -if asset.spawngroupname==Name then -return asset -end -end -return nil -end -function AUFTRAG:CountOpsGroups() -local N=0 -for _,_groupdata in pairs(self.groupdata)do -local groupdata=_groupdata -if groupdata and groupdata.opsgroup and groupdata.opsgroup:IsAlive()and not groupdata.opsgroup:IsDead()then -N=N+1 -end -end -return N -end -function AUFTRAG:GetMissionTypesText(MissionTypes) -local text="" -for _,missiontype in pairs(MissionTypes)do -text=text..string.format("%s, ",missiontype) -end -return text -end -function AUFTRAG:SetMissionWaypointCoord(Coordinate) -self.missionWaypointCoord=Coordinate -end -function AUFTRAG:GetMissionWaypointCoord(group) -if self.missionWaypointCoord then -local coord=self.missionWaypointCoord -if self.missionAltitude then -coord.y=self.missionAltitude -end -return coord -end -local waypointcoord=group:GetCoordinate():GetIntermediateCoordinate(self:GetTargetCoordinate(),self.missionFraction) -local alt=waypointcoord.y -waypointcoord=ZONE_RADIUS:New("Temp",waypointcoord:GetVec2(),1000):GetRandomCoordinate():SetAltitude(alt,false) -if self.missionAltitude then -waypointcoord:SetAltitude(self.missionAltitude,true) -end -return waypointcoord -end -function AUFTRAG:_SetLogID() -self.lid=string.format("Auftrag #%d %s | ",self.auftragsnummer,tostring(self.type)) -return self -end -function AUFTRAG:UpdateMarker() -local text=string.format("%s %s: %s",self.name,self.type:upper(),self.status:upper()) -text=text..string.format("\n%s",self:GetTargetName()) -text=text..string.format("\nTargets %d/%d, Life Points=%d/%d",self:CountMissionTargets(),self:GetTargetInitialNumber(),self:GetTargetLife(),self:GetTargetInitialLife()) -text=text..string.format("\nFlights %d/%d",self:CountOpsGroups(),self.nassets) -if not self.marker then -local targetcoord=self:GetTargetCoordinate() -if self.markerCoaliton and self.markerCoaliton>=0 then -self.marker=MARKER:New(targetcoord,text):ReadOnly():ToCoalition(self.markerCoaliton) -else -self.marker=MARKER:New(targetcoord,text):ReadOnly():ToAll() -end -else -if self.marker:GetText()~=text then -self.marker:UpdateText(text) -end -end -return self -end -function AUFTRAG:GetDCSMissionTask(TaskControllable) -local DCStasks={} -if self.type==AUFTRAG.Type.ANTISHIP then -self:_GetDCSAttackTask(self.engageTarget,DCStasks) -elseif self.type==AUFTRAG.Type.AWACS then -local DCStask=CONTROLLABLE.EnRouteTaskAWACS(nil) -table.insert(self.enrouteTasks,DCStask) -elseif self.type==AUFTRAG.Type.BAI then -self:_GetDCSAttackTask(self.engageTarget,DCStasks) -elseif self.type==AUFTRAG.Type.BOMBING then -local DCStask=CONTROLLABLE.TaskBombing(nil,self:GetTargetVec2(),self.engageAsGroup,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType,Divebomb) -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.BOMBRUNWAY then -local DCStask=CONTROLLABLE.TaskBombingRunway(nil,self.engageTarget:GetObject(),self.engageWeaponType,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAsGroup) -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.BOMBCARPET then -local DCStask=CONTROLLABLE.TaskCarpetBombing(nil,self:GetTargetVec2(),self.engageAsGroup,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType,self.engageCarpetLength) -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.CAP then -local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil,self.engageZone:GetVec2(),self.engageZone:GetRadius(),self.engageTargetTypes,Priority) -table.insert(self.enrouteTasks,DCStask) -elseif self.type==AUFTRAG.Type.CAS then -local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil,self.engageZone:GetVec2(),self.engageZone:GetRadius(),self.engageTargetTypes,Priority) -table.insert(self.enrouteTasks,DCStask) -elseif self.type==AUFTRAG.Type.ESCORT then -local DCStask=CONTROLLABLE.TaskEscort(nil,self.engageTarget:GetObject(),self.escortVec3,LastWaypointIndex,self.engageMaxDistance,self.engageTargetTypes) -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.FACA then -local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil,self.engageTarget:GetObject(),self.engageWeaponType,self.facDesignation,self.facDatalink,self.facFreq,self.facModu,CallsignName,CallsignNumber) -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.FERRY then -elseif self.type==AUFTRAG.Type.INTERCEPT then -self:_GetDCSAttackTask(self.engageTarget,DCStasks) -elseif self.type==AUFTRAG.Type.ORBIT then -elseif self.type==AUFTRAG.Type.GCICAP then -elseif self.type==AUFTRAG.Type.RECON then -elseif self.type==AUFTRAG.Type.SEAD then -self:_GetDCSAttackTask(self.engageTarget,DCStasks) -elseif self.type==AUFTRAG.Type.STRIKE then -local DCStask=CONTROLLABLE.TaskAttackMapObject(nil,self:GetTargetVec2(),self.engageAsGroup,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType) -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.TANKER then -local DCStask=CONTROLLABLE.EnRouteTaskTanker(nil) -table.insert(self.enrouteTasks,DCStask) -elseif self.type==AUFTRAG.Type.TROOPTRANSPORT then -local TaskEmbark=CONTROLLABLE.TaskEmbarking(TaskControllable,self.transportPickup,self.transportGroupSet,self.transportWaitForCargo) -local TaskDisEmbark=CONTROLLABLE.TaskDisembarking(TaskControllable,self.transportDropoff,self.transportGroupSet) -table.insert(DCStasks,TaskEmbark) -table.insert(DCStasks,TaskDisEmbark) -elseif self.type==AUFTRAG.Type.RESCUEHELO then -local DCStask={} -DCStask.id="Formation" -local param={} -param.unitname=self:GetTargetName() -param.offsetX=200 -param.offsetZ=240 -param.altitude=70 -param.dtFollow=1.0 -DCStask.params=param -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.ARTY then -local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,self:GetTargetVec2(),self.artyRadius,self.artyShots,self.engageWeaponType) -table.insert(DCStasks,DCStask) -elseif self.type==AUFTRAG.Type.PATROLZONE then -local DCStask={} -DCStask.id="PatrolZone" -local param={} -param.zone=self:GetObjective() -param.altitude=self.missionAltitude -param.speed=self.missionSpeed -DCStask.params=param -table.insert(DCStasks,DCStask) -else -self:E(self.lid..string.format("ERROR: Unknown mission task!")) -return nil -end -if self.type==AUFTRAG.Type.ORBIT or -self.type==AUFTRAG.Type.CAP or -self.type==AUFTRAG.Type.CAS or -self.type==AUFTRAG.Type.GCICAP or -self.type==AUFTRAG.Type.AWACS or -self.type==AUFTRAG.Type.TANKER then -local Coordinate=self:GetTargetCoordinate() -local DCStask=CONTROLLABLE.TaskOrbit(nil,Coordinate,self.orbitAltitude,self.orbitSpeed,self.orbitRaceTrack) -table.insert(DCStasks,DCStask) -end -self:T3({missiontask=DCStasks}) -if#DCStasks==1 then -return DCStasks[1] -else -return CONTROLLABLE.TaskCombo(nil,DCStasks) -end -end -function AUFTRAG:_GetDCSAttackTask(Target,DCStasks) -DCStasks=DCStasks or{} -for _,_target in pairs(Target.targets)do -local target=_target -if target.Type==TARGET.ObjectType.GROUP then -local DCStask=CONTROLLABLE.TaskAttackGroup(nil,target.Object,self.engageWeaponType,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageAsGroup) -table.insert(DCStasks,DCStask) -elseif target.Type==TARGET.ObjectType.UNIT or target.Type==TARGET.ObjectType.STATIC then -local DCStask=CONTROLLABLE.TaskAttackUnit(nil,target.Object,self.engageAsGroup,self.WeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType) -table.insert(DCStasks,DCStask) -end -end -return DCStasks -end -TARGET={ -ClassName="TARGET", -verbose=0, -lid=nil, -targets={}, -targetcounter=0, -life=0, -life0=0, -N0=0, -Ntargets0=0, -Ndestroyed=0, -Ndead=0, -elements={}, -casualties={}, -threatlevel0=0 -} -TARGET.ObjectType={ -GROUP="Group", -UNIT="Unit", -STATIC="Static", -SCENERY="Scenery", -COORDINATE="Coordinate", -AIRBASE="Airbase", -ZONE="Zone", -} -TARGET.Category={ -AIRCRAFT="Aircraft", -GROUND="Ground", -NAVAL="Naval", -AIRBASE="Airbase", -COORDINATE="Coordinate", -ZONE="Zone", -} -TARGET.ObjectStatus={ -ALIVE="Alive", -DEAD="Dead", -} -_TARGETID=0 -TARGET.version="0.3.1" -function TARGET:New(TargetObject) -local self=BASE:Inherit(self,FSM:New()) -_TARGETID=_TARGETID+1 -self:AddObject(TargetObject) -local Target=self.targets[1] -if not Target then -self:E("ERROR: No valid TARGET!") -return nil -end -self.name=self:GetTargetName(Target) -self.category=self:GetTargetCategory(Target) -self.lid=string.format("TARGET #%03d [%s] | ",_TARGETID,tostring(self.category)) -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Alive") -self:AddTransition("*","Status","*") -self:AddTransition("*","Stop","Stopped") -self:AddTransition("*","ObjectDamaged","*") -self:AddTransition("*","ObjectDestroyed","*") -self:AddTransition("*","ObjectDead","*") -self:AddTransition("*","Damaged","*") -self:AddTransition("*","Destroyed","Dead") -self:AddTransition("*","Dead","Dead") -self:__Start(-1) -return self -end -function TARGET:AddObject(Object) -if Object:IsInstanceOf("SET_GROUP")or Object:IsInstanceOf("SET_UNIT")then -local set=Object -for _,object in pairs(set.Set)do -self:AddObject(object) -end -else -self:_AddObject(Object) -end -end -function TARGET:IsAlive() -return self:Is("Alive") -end -function TARGET:IsDead() -return self:Is("Dead") -end -function TARGET:onafterStart(From,Event,To) -local text=string.format("Starting Target") -self:T(self.lid..text) -self:HandleEvent(EVENTS.Dead,self.OnEventUnitDeadOrLost) -self:HandleEvent(EVENTS.UnitLost,self.OnEventUnitDeadOrLost) -self:HandleEvent(EVENTS.RemoveUnit,self.OnEventUnitDeadOrLost) -self:__Status(-1) -end -function TARGET:onafterStatus(From,Event,To) -local fsmstate=self:GetState() -local damaged=false -for i,_target in pairs(self.targets)do -local target=_target -local life=target.Life -target.Life=self:GetTargetLife(target) -if target.Life=1 then -local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f",fsmstate,self:CountTargets(),self.N0,self:GetLife(),self:GetLife0(),self:GetDamage()) -if damaged then -text=text.." Damaged!" -end -self:I(self.lid..text) -end -if self.verbose>=2 then -local text="Target:" -for i,_target in pairs(self.targets)do -local target=_target -local damage=(1-target.Life/target.Life0)*100 -text=text..string.format("\n[%d] %s %s %s: Life=%.1f/%.1f, Damage=%.1f",i,target.Type,target.Name,target.Status,target.Life,target.Life0,damage) -end -self:I(self.lid..text) -end -if self:IsAlive()then -self:__Status(-30) -end -end -function TARGET:onafterObjectDamaged(From,Event,To,Target) -self:T(self.lid..string.format("Object %s damaged",Target.Name)) -end -function TARGET:onafterObjectDestroyed(From,Event,To,Target) -self:T(self.lid..string.format("Object %s destroyed",Target.Name)) -self.Ndestroyed=self.Ndestroyed+1 -self:ObjectDead(Target) -end -function TARGET:onafterObjectDead(From,Event,To,Target) -self:T(self.lid..string.format("Object %s dead",Target.Name)) -Target.Status=TARGET.ObjectStatus.DEAD -self.Ndead=self.Ndead+1 -local dead=true -for _,_target in pairs(self.targets)do -local target=_target -if target.Status==TARGET.ObjectStatus.ALIVE then -dead=false -end -end -if dead then -if self.Ndestroyed==self.Ntargets0 then -self:Destroyed() -else -self:Dead() -end -end -end -function TARGET:onafterDamaged(From,Event,To) -self:T(self.lid..string.format("TARGET damaged")) -end -function TARGET:onafterDestroyed(From,Event,To) -self:T(self.lid..string.format("TARGET destroyed")) -self:Dead() -end -function TARGET:onafterDead(From,Event,To) -self:T(self.lid..string.format("TARGET dead")) -end -function TARGET:OnEventUnitDeadOrLost(EventData) -local Name=EventData and EventData.IniUnitName or nil -if self:IsElement(Name)and not self:IsCasualty(Name)then -self:T3(self.lid..string.format("EVENT ID=%d: Unit %s dead or lost!",EventData.id,tostring(Name))) -table.insert(self.casualties,Name) -local target=self:GetTargetByName(EventData.IniGroupName) -if not target then -target=self:GetTargetByName(EventData.IniUnitName) -end -if target then -if EventData.id==EVENTS.RemoveUnit then -target.Ndead=target.Ndead+1 -else -target.Ndestroyed=target.Ndestroyed+1 -target.Ndead=target.Ndead+1 -end -if target.Ndead==target.N0 then -if target.Ndestroyed>=target.N0 then -self:T2(self.lid..string.format("EVENT ID=%d: target %s dead/lost ==> destroyed",EventData.id,tostring(target.Name))) -self:ObjectDestroyed(target) -else -self:T2(self.lid..string.format("EVENT ID=%d: target %s removed ==> dead",EventData.id,tostring(target.Name))) -self:ObjectDead(target) -end -end -end -end -end -function TARGET:_AddObject(Object) -local target={} -target.N0=0 -target.Ndead=0 -target.Ndestroyed=0 -if Object:IsInstanceOf("GROUP")then -local group=Object -target.Type=TARGET.ObjectType.GROUP -target.Name=group:GetName() -target.Coordinate=group:GetCoordinate() -local units=group:GetUnits() -target.Life=0;target.Life0=0 -for _,_unit in pairs(units or{})do -local unit=_unit -local life=unit:GetLife() -target.Life=target.Life+life -target.Life0=target.Life0+math.max(unit:GetLife0(),life) -self.threatlevel0=self.threatlevel0+unit:GetThreatLevel() -table.insert(self.elements,unit:GetName()) -target.N0=target.N0+1 -end -elseif Object:IsInstanceOf("UNIT")then -local unit=Object -target.Type=TARGET.ObjectType.UNIT -target.Name=unit:GetName() -target.Coordinate=unit:GetCoordinate() -if unit then -target.Life=unit:GetLife() -target.Life0=math.max(unit:GetLife0(),target.Life) -self.threatlevel0=self.threatlevel0+unit:GetThreatLevel() -table.insert(self.elements,unit:GetName()) -target.N0=target.N0+1 -end -elseif Object:IsInstanceOf("STATIC")then -local static=Object -target.Type=TARGET.ObjectType.STATIC -target.Name=static:GetName() -target.Coordinate=static:GetCoordinate() -if static and static:IsAlive()then -target.Life0=1 -target.Life=1 -target.N0=target.N0+1 -table.insert(self.elements,target.Name) -end -elseif Object:IsInstanceOf("SCENERY")then -local scenery=Object -target.Type=TARGET.ObjectType.SCENERY -target.Name=scenery:GetName() -target.Coordinate=scenery:GetCoordinate() -target.Life0=1 -target.Life=1 -target.N0=target.N0+1 -table.insert(self.elements,target.Name) -elseif Object:IsInstanceOf("AIRBASE")then -local airbase=Object -target.Type=TARGET.ObjectType.AIRBASE -target.Name=airbase:GetName() -target.Coordinate=airbase:GetCoordinate() -target.Life0=1 -target.Life=1 -target.N0=target.N0+1 -table.insert(self.elements,target.Name) -elseif Object:IsInstanceOf("COORDINATE")then -local coord=UTILS.DeepCopy(Object) -target.Type=TARGET.ObjectType.COORDINATE -target.Name=coord:ToStringMGRS() -target.Coordinate=coord -target.Life0=1 -target.Life=1 -elseif Object:IsInstanceOf("ZONE_BASE")then -local zone=Object -Object=zone -target.Type=TARGET.ObjectType.ZONE -target.Name=zone:GetName() -target.Coordinate=zone:GetCoordinate() -target.Life0=1 -target.Life=1 -else -self:E(self.lid.."ERROR: Unknown object type!") -return nil -end -self.life=self.life+target.Life -self.life0=self.life0+target.Life0 -self.N0=self.N0+target.N0 -self.Ntargets0=self.Ntargets0+1 -self.targetcounter=self.targetcounter+1 -target.ID=self.targetcounter -target.Status=TARGET.ObjectStatus.ALIVE -target.Object=Object -table.insert(self.targets,target) -end -function TARGET:GetLife0() -return self.life0 -end -function TARGET:GetDamage() -local life=self:GetLife()/self:GetLife0() -local damage=1-life -return damage*100 -end -function TARGET:GetTargetLife(Target) -if Target.Type==TARGET.ObjectType.GROUP then -if Target.Object and Target.Object:IsAlive()then -local units=Target.Object:GetUnits() -local life=0 -for _,_unit in pairs(units or{})do -local unit=_unit -life=life+unit:GetLife() -end -return life -else -return 0 -end -elseif Target.Type==TARGET.ObjectType.UNIT then -local unit=Target.Object -if unit and unit:IsAlive()then -local life=unit:GetLife() -return life -else -return 0 -end -elseif Target.Type==TARGET.ObjectType.STATIC then -if Target.Object and Target.Object:IsAlive()then -return 1 -else -return 0 -end -elseif Target.Type==TARGET.ObjectType.SCENERY then -if Target.Status==TARGET.ObjectStatus.ALIVE then -return 1 -else -return 0 -end -elseif Target.Type==TARGET.ObjectType.AIRBASE then -if Target.Status==TARGET.ObjectStatus.ALIVE then -return 1 -else -return 0 -end -elseif Target.Type==TARGET.ObjectType.COORDINATE then -return 1 -elseif Target.Type==TARGET.ObjectType.ZONE then -return 1 -else -self:E("ERROR: unknown target object type in GetTargetLife!") -end -end -function TARGET:GetLife() -local N=0 -for _,_target in pairs(self.targets)do -local Target=_target -N=N+self:GetTargetLife(Target) -end -return N -end -function TARGET:GetTargetVec3(Target) -if Target.Type==TARGET.ObjectType.GROUP then -local object=Target.Object -if object and object:IsAlive()then -local vec3=object:GetVec3() -return vec3 -else -return nil -end -elseif Target.Type==TARGET.ObjectType.UNIT then -local object=Target.Object -if object and object:IsAlive()then -local vec3=object:GetVec3() -return vec3 -else -return nil -end -elseif Target.Type==TARGET.ObjectType.STATIC then -local object=Target.Object -if object and object:IsAlive()then -local vec3=object:GetVec3() -return vec3 -else -return nil -end -elseif Target.Type==TARGET.ObjectType.SCENERY then -local object=Target.Object -if object then -local vec3=object:GetVec3() -return vec3 -else -return nil -end -elseif Target.Type==TARGET.ObjectType.AIRBASE then -local object=Target.Object -local vec3=object:GetVec3() -return vec3 -elseif Target.Type==TARGET.ObjectType.COORDINATE then -local object=Target.Object -local vec3={x=object.x,y=object.y,z=object.z} -return vec3 -elseif Target.Type==TARGET.ObjectType.ZONE then -local object=Target.Object -local vec3=object:GetVec3() -return vec3 -end -self:E(self.lid.."ERROR: Unknown TARGET type! Cannot get Vec3") -end -function TARGET:GetTargetCoordinate(Target) -if Target.Type==TARGET.ObjectType.COORDINATE then -return Target.Object -else -local vec3=self:GetTargetVec3(Target) -if vec3 then -Target.Coordinate.x=vec3.x -Target.Coordinate.y=vec3.y -Target.Coordinate.z=vec3.z -end -return Target.Coordinate -end -return nil -end -function TARGET:GetTargetName(Target) -if Target.Type==TARGET.ObjectType.GROUP then -if Target.Object and Target.Object:IsAlive()then -return Target.Object:GetName() -end -elseif Target.Type==TARGET.ObjectType.UNIT then -if Target.Object and Target.Object:IsAlive()then -return Target.Object:GetName() -end -elseif Target.Type==TARGET.ObjectType.STATIC then -if Target.Object and Target.Object:IsAlive()then -return Target.Object:GetName() -end -elseif Target.Type==TARGET.ObjectType.AIRBASE then -if Target.Status==TARGET.ObjectStatus.ALIVE then -return Target.Object:GetName() -end -elseif Target.Type==TARGET.ObjectType.COORDINATE then -local coord=Target.Object -return coord:ToStringMGRS() -end -return"Unknown" -end -function TARGET:GetName() -return self.name -end -function TARGET:GetCoordinate() -for _,_target in pairs(self.targets)do -local Target=_target -local coordinate=self:GetTargetCoordinate(Target) -if coordinate then -return coordinate -end -end -self:E(self.lid..string.format("ERROR: Cannot get coordinate of target %s",self.name)) -return nil -end -function TARGET:GetTargetCategory(Target) -local category=nil -if Target.Type==TARGET.ObjectType.GROUP then -if Target.Object and Target.Object:IsAlive()~=nil then -local group=Target.Object -local cat=group:GetCategory() -if cat==Group.Category.AIRPLANE or cat==Group.Category.HELICOPTER then -category=TARGET.Category.AIRCRAFT -elseif cat==Group.Category.GROUND or cat==Group.Category.TRAIN then -category=TARGET.Category.GROUND -elseif cat==Group.Category.SHIP then -category=TARGET.Category.NAVAL -end -end -elseif Target.Type==TARGET.ObjectType.UNIT then -if Target.Object and Target.Object:IsAlive()~=nil then -local unit=Target.Object -local group=unit:GetGroup() -local cat=group:GetCategory() -if cat==Group.Category.AIRPLANE or cat==Group.Category.HELICOPTER then -category=TARGET.Category.AIRCRAFT -elseif cat==Group.Category.GROUND or cat==Group.Category.TRAIN then -category=TARGET.Category.GROUND -elseif cat==Group.Category.SHIP then -category=TARGET.Category.NAVAL -end -end -elseif Target.Type==TARGET.ObjectType.STATIC then -return TARGET.Category.GROUND -elseif Target.Type==TARGET.ObjectType.SCENERY then -return TARGET.Category.GROUND -elseif Target.Type==TARGET.ObjectType.AIRBASE then -return TARGET.Category.AIRBASE -elseif Target.Type==TARGET.ObjectType.COORDINATE then -return TARGET.Category.COORDINATE -elseif Target.Type==TARGET.ObjectType.ZONE then -return TARGET.Category.ZONE -else -self:E("ERROR: unknown target category!") -end -return category -end -function TARGET:GetTargetByName(ObjectName) -for _,_target in pairs(self.targets)do -local target=_target -if ObjectName==target.Name then -return target -end -end -return nil -end -function TARGET:GetObjective() -for _,_target in pairs(self.targets)do -local target=_target -if target.Status==TARGET.ObjectStatus.ALIVE then -return target -end -end -return nil -end -function TARGET:GetObject() -local target=self:GetObjective() -if target then -return target.Object -end -return nil -end -function TARGET:CountObjectives(Target) -local N=0 -if Target.Type==TARGET.ObjectType.GROUP then -local target=Target.Object -local units=target:GetUnits() -for _,_unit in pairs(units or{})do -local unit=_unit -if unit and unit:IsAlive()~=nil and unit:GetLife()>1 then -N=N+1 -end -end -elseif Target.Type==TARGET.ObjectType.UNIT then -local target=Target.Object -if target and target:IsAlive()~=nil and target:GetLife()>1 then -N=N+1 -end -elseif Target.Type==TARGET.ObjectType.STATIC then -local target=Target.Object -if target and target:IsAlive()then -N=N+1 -end -elseif Target.Type==TARGET.ObjectType.SCENERY then -if Target.Status==TARGET.ObjectStatus.ALIVE then -N=N+1 -end -elseif Target.Type==TARGET.ObjectType.AIRBASE then -if Target.Status==TARGET.ObjectStatus.ALIVE then -N=N+1 -end -elseif Target.Type==TARGET.ObjectType.COORDINATE then -elseif Target.Type==TARGET.ObjectType.ZONE then -else -self:E(self.lid.."ERROR: Unknown target type! Cannot count targets") -end -return N -end -function TARGET:CountTargets() -local N=0 -for _,_target in pairs(self.targets)do -local Target=_target -N=N+self:CountObjectives(Target) -end -return N -end -function TARGET:IsElement(Name) -if Name==nil then -return false -end -for _,name in pairs(self.elements)do -if name==Name then -return true -end -end -return false -end -function TARGET:IsCasualty(Name) -if Name==nil then -return false -end -for _,name in pairs(self.casualties)do -if name==Name then -return true -end -end -return false -end -OPSGROUP={ -ClassName="OPSGROUP", -Debug=false, -verbose=0, -lid=nil, -groupname=nil, -group=nil, -template=nil, -isLateActivated=nil, -waypoints=nil, -waypoints0=nil, -currentwp=1, -elements={}, -taskqueue={}, -taskcounter=nil, -taskcurrent=nil, -taskenroute=nil, -taskpaused={}, -missionqueue={}, -currentmission=nil, -detectedunits={}, -detectedgroups={}, -attribute=nil, -checkzones=nil, -inzones=nil, -groupinitialized=nil, -respawning=nil, -wpcounter=1, -radio={}, -option={}, -optionDefault={}, -tacan={}, -icls={}, -callsign={}, -Ndestroyed=0, -Nkills=0, -weaponData={}, -} -OPSGROUP.ElementStatus={ -INUTERO="inutero", -SPAWNED="spawned", -PARKING="parking", -ENGINEON="engineon", -TAXIING="taxiing", -TAKEOFF="takeoff", -AIRBORNE="airborne", -LANDING="landing", -LANDED="landed", -ARRIVED="arrived", -DEAD="dead", -} -OPSGROUP.TaskStatus={ -SCHEDULED="scheduled", -EXECUTING="executing", -PAUSED="paused", -DONE="done", -} -OPSGROUP.TaskType={ -SCHEDULED="scheduled", -WAYPOINT="waypoint", -} -OPSGROUP.version="0.7.1" -function OPSGROUP:New(Group) -local self=BASE:Inherit(self,FSM:New()) -if type(Group)=="string"then -self.groupname=Group -self.group=GROUP:FindByName(self.groupname) -else -self.group=Group -self.groupname=Group:GetName() -end -self.lid=string.format("OPSGROUP %s | ",tostring(self.groupname)) -if self.group then -if not self:IsExist()then -self:E(self.lid.."ERROR: GROUP does not exist! Returning nil") -return nil -end -end -self.detectedunits=SET_UNIT:New() -self.detectedgroups=SET_GROUP:New() -self.inzones=SET_ZONE:New() -self.spot={} -self.spot.On=false -self.spot.timer=TIMER:New(self._UpdateLaser,self) -self.spot.Coordinate=COORDINATE:New(0,0,0) -self:SetLaser(1688,true,false,0.5) -self.taskcurrent=0 -self.taskcounter=0 -self:SetStartState("InUtero") -self:AddTransition("InUtero","Spawned","Spawned") -self:AddTransition("*","Dead","Dead") -self:AddTransition("*","Stop","Stopped") -self:AddTransition("*","Status","*") -self:AddTransition("*","Destroyed","*") -self:AddTransition("*","Damaged","*") -self:AddTransition("*","UpdateRoute","*") -self:AddTransition("*","Respawn","*") -self:AddTransition("*","PassingWaypoint","*") -self:AddTransition("*","DetectedUnit","*") -self:AddTransition("*","DetectedUnitNew","*") -self:AddTransition("*","DetectedUnitKnown","*") -self:AddTransition("*","DetectedUnitLost","*") -self:AddTransition("*","DetectedGroup","*") -self:AddTransition("*","DetectedGroupNew","*") -self:AddTransition("*","DetectedGroupKnown","*") -self:AddTransition("*","DetectedGroupLost","*") -self:AddTransition("*","PassingWaypoint","*") -self:AddTransition("*","GotoWaypoint","*") -self:AddTransition("*","OutOfAmmo","*") -self:AddTransition("*","OutOfGuns","*") -self:AddTransition("*","OutOfRockets","*") -self:AddTransition("*","OutOfBombs","*") -self:AddTransition("*","OutOfMissiles","*") -self:AddTransition("*","EnterZone","*") -self:AddTransition("*","LeaveZone","*") -self:AddTransition("*","LaserOn","*") -self:AddTransition("*","LaserOff","*") -self:AddTransition("*","LaserCode","*") -self:AddTransition("*","LaserPause","*") -self:AddTransition("*","LaserResume","*") -self:AddTransition("*","LaserLostLOS","*") -self:AddTransition("*","LaserGotLOS","*") -self:AddTransition("*","TaskExecute","*") -self:AddTransition("*","TaskPause","*") -self:AddTransition("*","TaskCancel","*") -self:AddTransition("*","TaskDone","*") -self:AddTransition("*","MissionStart","*") -self:AddTransition("*","MissionExecute","*") -self:AddTransition("*","MissionCancel","*") -self:AddTransition("*","PauseMission","*") -self:AddTransition("*","UnpauseMission","*") -self:AddTransition("*","MissionDone","*") -self:AddTransition("*","ElementSpawned","*") -self:AddTransition("*","ElementDestroyed","*") -self:AddTransition("*","ElementDead","*") -self:AddTransition("*","ElementDamaged","*") -return self -end -function OPSGROUP:GetCoalition() -return self.group:GetCoalition() -end -function OPSGROUP:GetLifePoints() -if self.group then -return self.group:GetLife(),self.group:GetLife0() -end -end -function OPSGROUP:SetVerbosity(VerbosityLevel) -self.verbose=VerbosityLevel or 0 -return self -end -function OPSGROUP:SetDefaultSpeed(Speed) -if Speed then -self.speedCruise=UTILS.KnotsToKmph(Speed) -end -return self -end -function OPSGROUP:GetSpeedCruise() -return UTILS.KmphToKnots(self.speedCruise or self.speedMax*0.7) -end -function OPSGROUP:SetDetection(Switch) -self.detectionOn=Switch -return self -end -function OPSGROUP:SetLaser(Code,CheckLOS,IROff,UpdateTime) -self.spot.Code=Code or 1688 -if CheckLOS~=nil then -self.spot.CheckLOS=CheckLOS -else -self.spot.CheckLOS=true -end -self.spot.IRon=not IROff -self.spot.dt=UpdateTime or 0.5 -return self -end -function OPSGROUP:GetLaserCode() -return self.spot.Code -end -function OPSGROUP:GetLaserCoordinate() -return self.spot.Coordinate -end -function OPSGROUP:GetLaserTarget() -return self.spot.TargetUnit -end -function OPSGROUP:SetCheckZones(CheckZonesSet) -self.checkzones=CheckZonesSet -return self -end -function OPSGROUP:AddCheckZone(CheckZone) -if not self.checkzones then -self.checkzones=SET_ZONE:New() -end -self.checkzones:AddZone(CheckZone) -return self -end -function OPSGROUP:AddWeaponRange(RangeMin,RangeMax,BitType) -RangeMin=UTILS.NMToMeters(RangeMin or 0) -RangeMax=UTILS.NMToMeters(RangeMax or 10) -local weapon={} -weapon.BitType=BitType or ENUMS.WeaponFlag.Auto -weapon.RangeMax=RangeMax -weapon.RangeMin=RangeMin -self.weaponData=self.weaponData or{} -self.weaponData[weapon.BitType]=weapon -return self -end -function OPSGROUP:GetWeaponData(BitType) -BitType=BitType or ENUMS.WeaponFlag.Auto -if self.weaponData[BitType]then -return self.weaponData[BitType] -else -return self.weaponData[ENUMS.WeaponFlag.Auto] -end -end -function OPSGROUP:GetDetectedUnits() -return self.detectedunits or{} -end -function OPSGROUP:GetDetectedGroups() -return self.detectedgroups or{} -end -function OPSGROUP:GetAmmo0() -return self.ammo -end -function OPSGROUP:GetThreat(ThreatLevelMin,ThreatLevelMax) -ThreatLevelMin=ThreatLevelMin or 1 -ThreatLevelMax=ThreatLevelMax or 10 -local threat=nil -local level=0 -for _,_unit in pairs(self.detectedunits:GetSet())do -local unit=_unit -local threatlevel=unit:GetThreatLevel() -if threatlevel>=ThreatLevelMin and threatlevel<=ThreatLevelMax then -if threatlevellevelmax then -threat=unit -levelmax=threatlevel -end -end -return threat,levelmax -end -function OPSGROUP:HasLoS(Coordinate,Element,OffsetElement,OffsetCoordinate) -local Vec3=Coordinate:GetVec3() -if OffsetCoordinate then -Vec3=UTILS.VecAdd(Vec3,OffsetCoordinate) -end -local function checklos(element) -local vec3=element.unit:GetVec3() -if OffsetElement then -vec3=UTILS.VecAdd(vec3,OffsetElement) -end -local _los=land.isVisible(vec3,Vec3) -return _los -end -if Element then -local los=checklos(Element) -return los -else -for _,element in pairs(self.elements)do -local los=checklos(element) -if los then -return true -end -end -return false -end -return nil -end -function OPSGROUP:GetGroup() -return self.group -end -function OPSGROUP:GetName() -return self.groupname -end -function OPSGROUP:GetDCSGroup() -local DCSGroup=Group.getByName(self.groupname) -return DCSGroup -end -function OPSGROUP:GetUnit(UnitNumber) -local DCSUnit=self:GetDCSUnit(UnitNumber) -if DCSUnit then -local unit=UNIT:Find(DCSUnit) -return unit -end -return nil -end -function OPSGROUP:GetDCSUnit(UnitNumber) -local DCSGroup=self:GetDCSGroup() -if DCSGroup then -local unit=DCSGroup:getUnit(UnitNumber or 1) -return unit -end -return nil -end -function OPSGROUP:GetDCSUnits() -local DCSGroup=self:GetDCSGroup() -if DCSGroup then -local units=DCSGroup:getUnits() -return units -end -return nil -end -function OPSGROUP:Despawn(Delay,NoEventRemoveUnit) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,OPSGROUP.Despawn,self,0,NoEventRemoveUnit) -else -local DCSGroup=self:GetDCSGroup() -if DCSGroup then -DCSGroup:destroy() -if not NoEventRemoveUnit then -local units=self:GetDCSUnits() -local EventTime=timer.getTime() -for i=1,#units do -self:CreateEventRemoveUnit(EventTime,units[i]) -end -end -end -end -return self -end -function OPSGROUP:Destroy(Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,OPSGROUP.Destroy,self) -else -local DCSGroup=self:GetDCSGroup() -if DCSGroup then -self:T(self.lid.."Destroying group") -DCSGroup:destroy() -local units=self:GetDCSUnits() -local EventTime=timer.getTime() -for i=1,#units do -if self.isAircraft then -self:CreateEventUnitLost(EventTime,units[i]) -else -self:CreateEventDead(EventTime,units[i]) -end -end -end -end -return self -end -function OPSGROUP:DespawnElement(Element,Delay,NoEventRemoveUnit) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,OPSGROUP.DespawnElement,self,Element,0,NoEventRemoveUnit) -else -if Element then -local DCSunit=Unit.getByName(Element.name) -if DCSunit then -DCSunit:destroy() -if not NoEventRemoveUnit then -self:CreateEventRemoveUnit(timer.getTime(),DCSunit) -end -end -end -end -return self -end -function OPSGROUP:GetVec2() -local vec3=self:GetVec3() -if vec3 then -local vec2={x=vec3.x,y=vec3.z} -return vec2 -end -return nil -end -function OPSGROUP:GetVec3() -if self:IsExist()then -local unit=self:GetDCSUnit() -if unit then -local vec3=unit:getPoint() -return vec3 -end -end -return nil -end -function OPSGROUP:GetCoordinate(NewObject) -local vec3=self:GetVec3() -if vec3 then -self.coordinate=self.coordinate or COORDINATE:New(0,0,0) -self.coordinate.x=vec3.x -self.coordinate.y=vec3.y -self.coordinate.z=vec3.z -if NewObject then -local coord=COORDINATE:NewFromCoordinate(self.coordinate) -return coord -else -return self.coordinate -end -else -self:E(self.lid.."WARNING: Group is not alive. Cannot get coordinate!") -end -return nil -end -function OPSGROUP:GetVelocity() -if self:IsExist()then -local unit=self:GetDCSUnit(1) -if unit then -local velvec3=unit:getVelocity() -local vel=UTILS.VecNorm(velvec3) -return vel -end -else -self:E(self.lid.."WARNING: Group does not exist. Cannot get velocity!") -end -return nil -end -function OPSGROUP:GetHeading() -if self:IsExist()then -local unit=self:GetDCSUnit() -if unit then -local pos=unit:getPosition() -local heading=math.atan2(pos.x.z,pos.x.x) -if heading<0 then -heading=heading+2*math.pi -end -heading=math.deg(heading) -return heading -end -else -self:E(self.lid.."WARNING: Group does not exist. Cannot get heading!") -end -return nil -end -function OPSGROUP:GetOrientation() -if self:IsExist()then -local unit=self:GetDCSUnit() -if unit then -local pos=unit:getPosition() -return pos.x,pos.y,pos.z -end -else -self:E(self.lid.."WARNING: Group does not exist. Cannot get orientation!") -end -return nil -end -function OPSGROUP:GetOrientationX() -local X,Y,Z=self:GetOrientation() -return X -end -function OPSGROUP:CheckTaskDescriptionUnique(description) -for _,_task in pairs(self.taskqueue)do -local task=_task -if task.description==description then -return false -end -end -return true -end -function OPSGROUP:Activate(delay) -if delay and delay>0 then -self:T2(self.lid..string.format("Activating late activated group in %d seconds",delay)) -self:ScheduleOnce(delay,OPSGROUP.Activate,self) -else -if self:IsAlive()==false then -self:T(self.lid.."Activating late activated group") -self.group:Activate() -self.isLateActivated=false -elseif self:IsAlive()==true then -self:E(self.lid.."WARNING: Activating group that is already activated") -else -self:E(self.lid.."ERROR: Activating group that is does not exist!") -end -end -return self -end -function OPSGROUP:SelfDestruction(Delay,ExplosionPower) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,OPSGROUP.SelfDestruction,self,0,ExplosionPower) -else -for i,_element in pairs(self.elements)do -local element=_element -local unit=element.unit -if unit and unit:IsAlive()then -unit:Explode(ExplosionPower) -end -end -end -end -function OPSGROUP:IsExist() -local DCSGroup=self:GetDCSGroup() -if DCSGroup then -local exists=DCSGroup:isExist() -return exists -end -return nil -end -function OPSGROUP:IsActive() -end -function OPSGROUP:IsAlive() -if self.group then -local alive=self.group:IsAlive() -return alive -end -return nil -end -function OPSGROUP:IsLateActivated() -return self.isLateActivated -end -function OPSGROUP:IsInUtero() -return self:Is("InUtero") -end -function OPSGROUP:IsSpawned() -return self:Is("Spawned") -end -function OPSGROUP:IsDead() -return self:Is("Dead") -end -function OPSGROUP:IsStopped() -return self:Is("Stopped") -end -function OPSGROUP:IsUncontrolled() -return self.isUncontrolled -end -function OPSGROUP:HasPassedFinalWaypoint() -return self.passedfinalwp -end -function OPSGROUP:IsRearming() -local rearming=self:Is("Rearming")or self:Is("Rearm") -return rearming -end -function OPSGROUP:IsLasing() -return self.spot.On -end -function OPSGROUP:IsRetreating() -return self:is("Retreating") -end -function OPSGROUP:IsEngaging() -return self:is("Engaging") -end -function OPSGROUP:GetWaypoints() -return self.waypoints -end -function OPSGROUP:MarkWaypoints(Duration) -for i,_waypoint in pairs(self.waypoints or{})do -local waypoint=_waypoint -local text=string.format("Waypoint ID=%d of %s",waypoint.uid,self.groupname) -text=text..string.format("\nSpeed=%.1f kts, Alt=%d ft (%s)",UTILS.MpsToKnots(waypoint.speed),UTILS.MetersToFeet(waypoint.alt),"BARO") -if waypoint.marker then -if waypoint.marker.text~=text then -waypoint.marker.text=text -end -else -waypoint.marker=MARKER:New(waypoint.coordinate,text):ToCoalition(self:GetCoalition()) -end -end -if Duration then -self:RemoveWaypointMarkers(Duration) -end -return self -end -function OPSGROUP:RemoveWaypointMarkers(Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,OPSGROUP.RemoveWaypointMarkers,self) -else -for i,_waypoint in pairs(self.waypoints or{})do -local waypoint=_waypoint -if waypoint.marker then -waypoint.marker:Remove() -end -end -end -return self -end -function OPSGROUP:GetWaypointByID(uid) -for _,_waypoint in pairs(self.waypoints or{})do -local waypoint=_waypoint -if waypoint.uid==uid then -return waypoint -end -end -return nil -end -function OPSGROUP:GetWaypointByIndex(index) -for i,_waypoint in pairs(self.waypoints)do -local waypoint=_waypoint -if i==index then -return waypoint -end -end -return nil -end -function OPSGROUP:GetWaypointUIDFromIndex(index) -for i,_waypoint in pairs(self.waypoints)do -local waypoint=_waypoint -if i==index then -return waypoint.uid -end -end -return nil -end -function OPSGROUP:GetWaypointIndex(uid) -if uid then -for i,_waypoint in pairs(self.waypoints or{})do -local waypoint=_waypoint -if waypoint.uid==uid then -return i -end -end -end -return nil -end -function OPSGROUP:GetWaypointIndexNext(cyclic,i) -if cyclic==nil then -cyclic=self.adinfinitum -end -local N=#self.waypoints -i=i or self.currentwp -local n=math.min(i+1,N) -if cyclic and i==N then -n=1 -end -return n -end -function OPSGROUP:GetWaypointIndexCurrent() -return self.currentwp or 1 -end -function OPSGROUP:GetWaypointIndexAfterID(uid) -local index=self:GetWaypointIndex(uid) -if index then -return index+1 -else -return#self.waypoints+1 -end -end -function OPSGROUP:GetWaypoint(indx) -return self.waypoints[indx] -end -function OPSGROUP:GetWaypointFinal() -return self.waypoints[#self.waypoints] -end -function OPSGROUP:GetWaypointNext(cyclic) -local n=self:GetWaypointIndexNext(cyclic) -return self.waypoints[n] -end -function OPSGROUP:GetWaypointCurrent() -return self.waypoints[self.currentwp] -end -function OPSGROUP:GetNextWaypointCoordinate(cyclic) -local waypoint=self:GetWaypointNext(cyclic) -return waypoint.coordinate -end -function OPSGROUP:GetWaypointCoordinate(index) -local waypoint=self:GetWaypoint(index) -if waypoint then -return waypoint.coordinate -end -return nil -end -function OPSGROUP:GetWaypointSpeed(indx) -local waypoint=self:GetWaypoint(indx) -if waypoint then -return UTILS.MpsToKnots(waypoint.speed) -end -return nil -end -function OPSGROUP:GetWaypointUID(waypoint) -return waypoint.uid -end -function OPSGROUP:GetWaypointID(indx) -local waypoint=self:GetWaypoint(indx) -if waypoint then -return waypoint.uid -end -return nil -end -function OPSGROUP:GetSpeedToWaypoint(indx) -local speed=self:GetWaypointSpeed(indx) -if speed<=0.1 then -speed=self:GetSpeedCruise() -end -return speed -end -function OPSGROUP:GetDistanceToWaypoint(indx) -local dist=0 -if#self.waypoints>0 then -indx=indx or self:GetWaypointIndexNext() -local wp=self:GetWaypoint(indx) -if wp then -local coord=self:GetCoordinate() -dist=coord:Get2DDistance(wp.coordinate) -end -end -return dist -end -function OPSGROUP:GetTimeToWaypoint(indx) -local s=self:GetDistanceToWaypoint(indx) -local v=self:GetVelocity() -local t=s/v -if t==math.inf then -return 365*24*60*60 -elseif t==math.nan then -return 0 -else -return t -end -end -function OPSGROUP:GetExpectedSpeed() -if self:IsHolding()then -return 0 -else -return self.speedWp or 0 -end -end -function OPSGROUP:RemoveWaypointByID(uid) -local index=self:GetWaypointIndex(uid) -if index then -self:RemoveWaypoint(index) -end -return self -end -function OPSGROUP:RemoveWaypoint(wpindex) -if self.waypoints then -local N=#self.waypoints -local wp=self:GetWaypoint(wpindex) -if wp and wp.marker then -wp.marker:Remove() -end -table.remove(self.waypoints,wpindex) -local n=#self.waypoints -self:T(self.lid..string.format("Removing waypoint index %d, current wp index %d. N %d-->%d",wpindex,self.currentwp,N,n)) -if wpindex>self.currentwp then -if self.currentwp>=n then -self.passedfinalwp=true -end -self:_CheckGroupDone(1) -else -if self.currentwp==1 then -if self.adinfinitum then -self.currentwp=#self.waypoints -else -self.currentwp=1 -end -else -self.currentwp=self.currentwp-1 -end -end -end -return self -end -function OPSGROUP:SetTask(DCSTask) -if self:IsAlive()then -if self.taskcurrent>0 then -end -if self.taskenroute and#self.taskenroute>0 then -if tostring(DCSTask.id)=="ComboTask"then -for _,task in pairs(self.taskenroute)do -table.insert(DCSTask.params.tasks,1,task) -end -else -local tasks=UTILS.DeepCopy(self.taskenroute) -table.insert(tasks,DCSTask) -DCSTask=self.group.TaskCombo(self,tasks) -end -end -self.group:SetTask(DCSTask) -local text=string.format("SETTING Task %s",tostring(DCSTask.id)) -if tostring(DCSTask.id)=="ComboTask"then -for i,task in pairs(DCSTask.params.tasks)do -text=text..string.format("\n[%d] %s",i,tostring(task.id)) -end -end -self:T(self.lid..text) -end -return self -end -function OPSGROUP:PushTask(DCSTask) -if self:IsAlive()then -self.group:PushTask(DCSTask) -local text=string.format("PUSHING Task %s",tostring(DCSTask.id)) -if tostring(DCSTask.id)=="ComboTask"then -for i,task in pairs(DCSTask.params.tasks)do -text=text..string.format("\n[%d] %s",i,tostring(task.id)) -end -end -self:T(self.lid..text) -end -return self -end -function OPSGROUP:ClearTasks() -if self:IsAlive()then -self.group:ClearTasks() -self:I(self.lid..string.format("CLEARING Tasks")) -end -return self -end -function OPSGROUP:AddTask(task,clock,description,prio,duration) -local newtask=self:NewTaskScheduled(task,clock,description,prio,duration) -table.insert(self.taskqueue,newtask) -self:T(self.lid..string.format("Adding SCHEDULED task %s starting at %s",newtask.description,UTILS.SecondsToClock(newtask.time,true))) -self:T3({newtask=newtask}) -return newtask -end -function OPSGROUP:NewTaskScheduled(task,clock,description,prio,duration) -self.taskcounter=self.taskcounter+1 -local time=timer.getAbsTime()+5 -if clock then -if type(clock)=="string"then -time=UTILS.ClockToSeconds(clock) -elseif type(clock)=="number"then -time=timer.getAbsTime()+clock -end -end -local newtask={} -newtask.status=OPSGROUP.TaskStatus.SCHEDULED -newtask.dcstask=task -newtask.description=description or task.id -newtask.prio=prio or 50 -newtask.time=time -newtask.id=self.taskcounter -newtask.duration=duration -newtask.waypoint=-1 -newtask.type=OPSGROUP.TaskType.SCHEDULED -newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d",self.groupname,newtask.id)) -newtask.stopflag:Set(0) -return newtask -end -function OPSGROUP:AddTaskWaypoint(task,Waypoint,description,prio,duration) -Waypoint=Waypoint or self:GetWaypointNext() -if Waypoint then -self.taskcounter=self.taskcounter+1 -local newtask={} -newtask.description=description or string.format("Task #%d",self.taskcounter) -newtask.status=OPSGROUP.TaskStatus.SCHEDULED -newtask.dcstask=task -newtask.prio=prio or 50 -newtask.id=self.taskcounter -newtask.duration=duration -newtask.time=0 -newtask.waypoint=Waypoint.uid -newtask.type=OPSGROUP.TaskType.WAYPOINT -newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d",self.groupname,newtask.id)) -newtask.stopflag:Set(0) -table.insert(self.taskqueue,newtask) -self:T(self.lid..string.format("Adding WAYPOINT task %s at WP ID=%d",newtask.description,newtask.waypoint)) -self:T3({newtask=newtask}) -self:__UpdateRoute(-1) -return newtask -end -return nil -end -function OPSGROUP:AddTaskEnroute(task) -if not self.taskenroute then -self.taskenroute={} -end -local gotit=false -for _,Task in pairs(self.taskenroute)do -if Task.id==task.id then -gotit=true -break -end -end -if not gotit then -table.insert(self.taskenroute,task) -end -end -function OPSGROUP:GetTasksWaypoint(id) -local tasks={} -self:_SortTaskQueue() -for _,_task in pairs(self.taskqueue)do -local task=_task -if task.type==OPSGROUP.TaskType.WAYPOINT and task.status==OPSGROUP.TaskStatus.SCHEDULED and task.waypoint==id then -table.insert(tasks,task) -end -end -return tasks -end -function OPSGROUP:CountTasksWaypoint(id) -local n=0 -for _,_task in pairs(self.taskqueue)do -local task=_task -if task.type==OPSGROUP.TaskType.WAYPOINT and task.status==OPSGROUP.TaskStatus.SCHEDULED and task.waypoint==id then -n=n+1 -end -end -return n -end -function OPSGROUP:_SortTaskQueue() -local function _sort(a,b) -local taskA=a -local taskB=b -return(taskA.prio=task.time then -return task -end -end -return nil -end -function OPSGROUP:GetTaskCurrent() -local task=self:GetTaskByID(self.taskcurrent,OPSGROUP.TaskStatus.EXECUTING) -return task -end -function OPSGROUP:GetTaskByID(id,status) -for _,_task in pairs(self.taskqueue)do -local task=_task -if task.id==id then -if status==nil or status==task.status then -return task -end -end -end -return nil -end -function OPSGROUP:onafterTaskExecute(From,Event,To,Task) -local text=string.format("Task %s ID=%d execute",tostring(Task.description),Task.id) -self:T(self.lid..text) -if self.taskcurrent>0 then -self:TaskCancel() -end -self.taskcurrent=Task.id -Task.timestamp=timer.getAbsTime() -Task.status=OPSGROUP.TaskStatus.EXECUTING -if Task.dcstask.id=="Formation"then -local followSet=SET_GROUP:New():AddGroup(self.group) -local param=Task.dcstask.params -local followUnit=UNIT:FindByName(param.unitname) -Task.formation=AI_FORMATION:New(followUnit,followSet,"Formation","Follow X at given parameters.") -Task.formation:FormationCenterWing(-param.offsetX,50,math.abs(param.altitude),50,param.offsetZ,50) -Task.formation:SetFollowTimeInterval(param.dtFollow) -Task.formation:SetFlightModeFormation(self.group) -Task.formation:Start() -elseif Task.dcstask.id=="PatrolZone"then -local zone=Task.dcstask.params.zone -local Coordinate=zone:GetRandomCoordinate() -local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise) -local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude)or nil -if self.isFlightgroup then -FLIGHTGROUP.AddWaypoint(self,Coordinate,Speed,AfterWaypointWithID,Altitude) -elseif self.isNavygroup then -ARMYGROUP.AddWaypoint(self,Coordinate,Speed,AfterWaypointWithID,Formation) -elseif self.isArmygroup then -NAVYGROUP.AddWaypoint(self,Coordinate,Speed,AfterWaypointWithID,Altitude) -end -else -if Task.type==OPSGROUP.TaskType.SCHEDULED then -local DCStasks={} -if Task.dcstask.id=='ComboTask'then -for TaskID,Task in ipairs(Task.dcstask.params.tasks)do -table.insert(DCStasks,Task) -end -else -table.insert(DCStasks,Task.dcstask) -end -local TaskCombo=self.group:TaskCombo(DCStasks) -local TaskCondition=self.group:TaskCondition(nil,Task.stopflag:GetName(),1,nil,Task.duration) -local TaskControlled=self.group:TaskControlled(TaskCombo,TaskCondition) -local TaskDone=self.group:TaskFunction("OPSGROUP._TaskDone",self,Task) -local TaskFinal=self.group:TaskCombo({TaskControlled,TaskDone}) -self:SetTask(TaskFinal) -end -end -local Mission=self:GetMissionByTaskID(self.taskcurrent) -if Mission then -self:MissionExecute(Mission) -end -end -function OPSGROUP:onafterTaskCancel(From,Event,To,Task) -local currenttask=self:GetTaskCurrent() -Task=Task or currenttask -if Task then -if currenttask and Task.id==currenttask.id then -local stopflag=Task.stopflag:Get() -local text=string.format("Current task %s ID=%d cancelled (flag %s=%d)",Task.description,Task.id,Task.stopflag:GetName(),stopflag) -self:T(self.lid..text) -Task.stopflag:Set(1) -local done=false -if Task.dcstask.id=="Formation"then -Task.formation:Stop() -done=true -elseif Task.dcstask.id=="PatrolZone"then -done=true -elseif stopflag==1 or(not self:IsAlive())or self:IsDead()or self:IsStopped()then -done=true -end -if done then -self:TaskDone(Task) -end -else -self:T(self.lid..string.format("TaskCancel: Setting task %s ID=%d to DONE",Task.description,Task.id)) -self:TaskDone(Task) -end -else -local text=string.format("WARNING: No (current) task to cancel!") -self:E(self.lid..text) -end -end -function OPSGROUP:onbeforeTaskDone(From,Event,To,Task) -local allowed=true -if Task.status==OPSGROUP.TaskStatus.PAUSED then -allowed=false -end -return allowed -end -function OPSGROUP:onafterTaskDone(From,Event,To,Task) -local text=string.format("Task done: %s ID=%d",Task.description,Task.id) -self:T(self.lid..text) -if Task.id==self.taskcurrent then -self.taskcurrent=0 -end -Task.status=OPSGROUP.TaskStatus.DONE -if Task.backupROE then -self:SwitchROE(Task.backupROE) -end -local Mission=self:GetMissionByTaskID(Task.id) -if Mission and Mission:IsNotOver()then -local status=Mission:GetGroupStatus(self) -if status~=AUFTRAG.GroupStatus.PAUSED then -self:T(self.lid.."Task Done ==> Mission Done!") -self:MissionDone(Mission) -else -end -else -if Task.description=="Engage_Target"then -self:Disengage() -end -self:T(self.lid.."Task Done but NO mission found ==> _CheckGroupDone in 1 sec") -self:_CheckGroupDone(1) -end -end -function OPSGROUP:AddMission(Mission) -Mission:AddOpsGroup(self) -Mission:SetGroupStatus(self,AUFTRAG.GroupStatus.SCHEDULED) -Mission:Scheduled() -Mission.Nelements=Mission.Nelements+#self.elements -table.insert(self.missionqueue,Mission) -local text=string.format("Added %s mission %s starting at %s, stopping at %s", -tostring(Mission.type),tostring(Mission.name),UTILS.SecondsToClock(Mission.Tstart,true),Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop,true)or"INF") -self:T(self.lid..text) -return self -end -function OPSGROUP:RemoveMission(Mission) -for i,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission.auftragsnummer==Mission.auftragsnummer then -local Task=Mission:GetGroupWaypointTask(self) -if Task then -self:RemoveTask(Task) -end -table.remove(self.missionqueue,i) -return self -end -end -return self -end -function OPSGROUP:CountRemainingMissison() -local N=0 -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission and mission:IsNotOver()then -local status=mission:GetGroupStatus(self) -if status~=AUFTRAG.GroupStatus.DONE and status~=AUFTRAG.GroupStatus.CANCELLED then -N=N+1 -end -end -end -return N -end -function OPSGROUP:_GetNextMission() -local Nmissions=#self.missionqueue -if Nmissions==0 then -return nil -end -local function _sort(a,b) -local taskA=a -local taskB=b -return(taskA.prio0 then -self:ScheduleOnce(delay,OPSGROUP.RouteToMission,self,mission) -else -if self:IsDead()then -return -end -local uid=self:GetWaypointCurrent().uid -local waypointcoord=mission:GetMissionWaypointCoord(self.group) -for _,task in pairs(mission.enrouteTasks)do -self:AddTaskEnroute(task) -end -local SpeedToMission=UTILS.KmphToKnots(self.speedCruise) -if mission.type==AUFTRAG.Type.TROOPTRANSPORT then -mission.DCStask=mission:GetDCSMissionTask(self.group) -for _,_group in pairs(mission.transportGroupSet.Set)do -local group=_group -if group and group:IsAlive()then -local DCSTask=group:TaskEmbarkToTransport(mission.transportPickup,500) -group:SetTask(DCSTask,5) -end -end -elseif mission.type==AUFTRAG.Type.ARTY then -local weapondata=self:GetWeaponData(mission.engageWeaponType) -if weapondata then -local targetcoord=mission:GetTargetCoordinate() -local heading=self:GetCoordinate():HeadingTo(targetcoord) -local dist=self:GetCoordinate():Get2DDistance(targetcoord) -if dist>weapondata.RangeMax then -local d=(dist-weapondata.RangeMax)*1.1 -waypointcoord=self:GetCoordinate():Translate(d,heading) -self:T(self.lid..string.format("Out of max range = %.1f km for weapon %d",weapondata.RangeMax/1000,mission.engageWeaponType)) -elseif dist0 then -for i,_task in pairs(tasks)do -local task=_task -text=text..string.format("\n[%d] %s",i,task.description) -end -else -text=text.." None" -end -self:T(self.lid..text) -local taskswp={} -for _,task in pairs(tasks)do -local Task=task -table.insert(taskswp,self.group:TaskFunction("OPSGROUP._TaskExecute",self,Task)) -local TaskCondition=self.group:TaskCondition(nil,Task.stopflag:GetName(),1,nil,Task.duration) -table.insert(taskswp,self.group:TaskControlled(Task.dcstask,TaskCondition)) -table.insert(taskswp,self.group:TaskFunction("OPSGROUP._TaskDone",self,Task)) -end -if#taskswp>0 then -self:SetTask(self.group:TaskCombo(taskswp)) -end -return#taskswp -end -function OPSGROUP:onafterGotoWaypoint(From,Event,To,UID) -local n=self:GetWaypointIndex(UID) -if n then -if false then -local tasks=self:GetTasksWaypoint(n) -for _,_task in pairs(tasks)do -local task=_task -task.status=OPSGROUP.TaskStatus.SCHEDULED -end -end -local Speed=self:GetSpeedToWaypoint(n) -self:__UpdateRoute(-1,n,Speed) -end -end -function OPSGROUP:onafterDetectedUnit(From,Event,To,Unit) -local unitname=Unit and Unit:GetName()or"unknown" -self:T2(self.lid..string.format("Detected unit %s",unitname)) -if self.detectedunits:FindUnit(unitname)then -self:DetectedUnitKnown(Unit) -else -self:DetectedUnitNew(Unit) -end -end -function OPSGROUP:onafterDetectedUnitNew(From,Event,To,Unit) -self:T(self.lid..string.format("Detected New unit %s",Unit:GetName())) -self.detectedunits:AddUnit(Unit) -end -function OPSGROUP:onafterDetectedGroup(From,Event,To,Group) -local groupname=Group and Group:GetName()or"unknown" -self:T(self.lid..string.format("Detected group %s",groupname)) -if self.detectedgroups:FindGroup(groupname)then -self:DetectedGroupKnown(Group) -else -self:DetectedGroupNew(Group) -end -end -function OPSGROUP:onafterDetectedGroupNew(From,Event,To,Group) -self:T(self.lid..string.format("Detected New group %s",Group:GetName())) -self.detectedgroups:AddGroup(Group) -end -function OPSGROUP:onafterEnterZone(From,Event,To,Zone) -local zonename=Zone and Zone:GetName()or"unknown" -self:T2(self.lid..string.format("Entered Zone %s",zonename)) -self.inzones:Add(Zone:GetName(),Zone) -end -function OPSGROUP:onafterLeaveZone(From,Event,To,Zone) -local zonename=Zone and Zone:GetName()or"unknown" -self:T2(self.lid..string.format("Left Zone %s",zonename)) -self.inzones:Remove(zonename,true) -end -function OPSGROUP:onbeforeLaserOn(From,Event,To,Target) -if self.spot.On then -return false -end -if Target then -self:SetLaserTarget(Target) -else -self:E(self.lid.."ERROR: No target provided for LASER!") -return false -end -local element=self:GetElementAlive() -if element then -self.spot.element=element -local offsetY=0 -if self.isGround or self.isNaval then -offsetY=element.height -end -self.spot.offset={x=0,y=offsetY,z=0} -if self.spot.CheckLOS then -local los=self:HasLoS(self.spot.Coordinate,self.spot.element,self.spot.offset) -if los then -self:LaserGotLOS() -else -self:I(self.lid.."LASER got no LOS currently. Trying to switch the laser on again in 10 sec") -self:__LaserOn(-10,Target) -return false -end -end -else -self:E(self.lid.."ERROR: No element alive for lasing") -return false -end -return true -end -function OPSGROUP:onafterLaserOn(From,Event,To,Target) -if not self.spot.timer:IsRunning()then -self.spot.timer:Start(nil,self.spot.dt) -end -local DCSunit=self.spot.element.unit:GetDCSObject() -self.spot.Laser=Spot.createLaser(DCSunit,self.spot.offset,self.spot.vec3,self.spot.Code or 1688) -if self.spot.IRon then -self.spot.IR=Spot.createInfraRed(DCSunit,self.spot.offset,self.spot.vec3) -end -self.spot.On=true -self.spot.Paused=false -self:T(self.lid.."Switching LASER on") -end -function OPSGROUP:onbeforeLaserOff(From,Event,To) -return self.spot.On or self.spot.Paused -end -function OPSGROUP:onafterLaserOff(From,Event,To) -self:T(self.lid.."Switching LASER off") -if self.spot.On then -self.spot.Laser:destroy() -self.spot.IR:destroy() -self.spot.Laser=nil -self.spot.IR=nil -end -self.spot.timer:Stop() -self.spot.TargetUnit=nil -self.spot.On=false -self.spot.Paused=false -end -function OPSGROUP:onafterLaserPause(From,Event,To) -self:T(self.lid.."Switching LASER off temporarily") -self.spot.Laser:destroy() -self.spot.IR:destroy() -self.spot.Laser=nil -self.spot.IR=nil -self.spot.On=false -self.spot.Paused=true -end -function OPSGROUP:onbeforeLaserResume(From,Event,To) -return self.spot.Paused -end -function OPSGROUP:onafterLaserResume(From,Event,To) -self:T(self.lid.."Resuming LASER") -self.spot.Paused=false -local target=nil -if self.spot.TargetType==0 then -target=self.spot.Coordinate -elseif self.spot.TargetType==1 or self.spot.TargetType==2 then -target=self.spot.TargetUnit -elseif self.spot.TargetType==3 then -target=self.spot.TargetGroup -end -if target then -self:T(self.lid.."Switching LASER on again") -self:LaserOn(target) -end -end -function OPSGROUP:onafterLaserCode(From,Event,To,Code) -self.spot.Code=Code or 1688 -self:T2(self.lid..string.format("Setting LASER Code to %d",self.spot.Code)) -if self.spot.On then -self:T(self.lid..string.format("New LASER Code is %d",self.spot.Code)) -self.spot.Laser:setCode(self.spot.Code) -end -end -function OPSGROUP:onafterLaserLostLOS(From,Event,To) -self.spot.LOS=false -self.spot.lostLOS=true -if self.spot.On then -self:LaserPause() -end -end -function OPSGROUP:onafterLaserGotLOS(From,Event,To) -self.spot.LOS=true -if self.spot.lostLOS then -self.spot.lostLOS=false -if self.spot.Paused then -self:LaserResume() -end -end -end -function OPSGROUP:SetLaserTarget(Target) -if Target then -if Target:IsInstanceOf("SCENERY")then -self.spot.TargetType=0 -self.spot.offsetTarget={x=0,y=1,z=0} -elseif Target:IsInstanceOf("POSITIONABLE")then -local target=Target -if target:IsAlive()then -if target:IsInstanceOf("GROUP")then -self.spot.TargetGroup=target -self.spot.TargetUnit=target:GetHighestThreat() -self.spot.TargetType=3 -else -self.spot.TargetUnit=target -if target:IsInstanceOf("STATIC")then -self.spot.TargetType=1 -elseif target:IsInstanceOf("UNIT")then -self.spot.TargetType=2 -end -end -local size,x,y,z=self.spot.TargetUnit:GetObjectSize() -if y then -self.spot.offsetTarget={x=0,y=y*0.75,z=0} -else -self.spot.offsetTarget={x=0,2,z=0} -end -else -self:E("WARNING: LASER target is not alive!") -return -end -elseif Target:IsInstanceOf("COORDINATE")then -self.spot.TargetType=0 -self.spot.offsetTarget={x=0,y=0,z=0} -else -self:E(self.lid.."ERROR: LASER target should be a POSITIONABLE (GROUP, UNIT or STATIC) or a COORDINATE object!") -return -end -self.spot.vec3=UTILS.VecAdd(Target:GetVec3(),self.spot.offsetTarget) -self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) -end -end -function OPSGROUP:_UpdateLaser() -if self.spot.TargetUnit then -if self.spot.TargetUnit:IsAlive()then -local vec3=self.spot.TargetUnit:GetVec3() -vec3=UTILS.VecAdd(vec3,self.spot.offsetTarget) -local dist=UTILS.VecDist3D(vec3,self.spot.vec3) -self.spot.vec3=vec3 -self.spot.Coordinate:UpdateFromVec3(vec3) -if dist>1 then -if self.spot.On then -self.spot.Laser:setPoint(vec3) -if self.spot.IRon then -self.spot.IR:setPoint(vec3) -end -end -end -else -if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive()then -local unit=self.spot.TargetGroup:GetHighestThreat() -if unit then -self:T(self.lid..string.format("Switching to target unit %s in the group",unit:GetName())) -self.spot.TargetUnit=unit -return -else -self:T(self.lid.."Target is not alive any more ==> switching LASER off") -self:LaserOff() -return -end -else -self:T(self.lid.."Target is not alive any more ==> switching LASER off") -self:LaserOff() -return -end -end -end -if self.spot.CheckLOS then -local los=self:HasLoS(self.spot.Coordinate,self.spot.element,self.spot.offset) -if los then -if self.spot.lostLOS then -self:LaserGotLOS() -end -else -if not self.spot.lostLOS then -self:LaserLostLOS() -end -end -end -end -function OPSGROUP:onafterElementDestroyed(From,Event,To,Element) -self:T(self.lid..string.format("Element destroyed %s",Element.name)) -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -mission:ElementDestroyed(self,Element) -end -self.Ndestroyed=self.Ndestroyed+1 -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.DEAD) -end -function OPSGROUP:onafterElementDead(From,Event,To,Element) -self:T(self.lid..string.format("Element dead %s at t=%.3f",Element.name,timer.getTime())) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.DEAD) -if self.spot.On and self.spot.element.name==Element.name then -self:LaserOff() -if self:GetNelements()>0 then -local target=nil -if self.spot.TargetType==0 then -target=self.spot.Coordinate -elseif self.spot.TargetType==1 or self.spot.TargetType==2 then -if self.spot.TargetUnit and self.spot.TargetUnit:IsAlive()then -target=self.spot.TargetUnit -end -elseif self.spot.TargetType==3 then -if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive()then -target=self.spot.TargetGroup -end -end -if target then -self:__LaserOn(-1,target) -end -end -end -end -function OPSGROUP:onbeforeDead(From,Event,To) -if self.Ndestroyed==#self.elements then -self:Destroyed() -end -end -function OPSGROUP:onafterDead(From,Event,To) -self:T(self.lid..string.format("Group dead at t=%.3f",timer.getTime())) -self.waypoints=nil -self.groupinitialized=false -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -self:T(self.lid.."Cancelling mission because group is dead! Mission name "..tostring(mission:GetName())) -self:MissionCancel(mission) -mission:GroupDead(self) -end -self:__Stop(-5) -end -function OPSGROUP:onafterStop(From,Event,To) -self.timerCheckZone:Stop() -self.timerQueueUpdate:Stop() -self.CallScheduler:Clear() -if self:IsAlive()and not(self:IsDead()or self:IsStopped())then -local life,life0=self:GetLifePoints() -local state=self:GetState() -local text=string.format("WARNING: Group is still alive! Current state=%s. Life points=%d/%d. Use OPSGROUP:Destroy() or OPSGROUP:Despawn() for a clean stop",state,life,life0) -self:E(self.lid..text) -end -self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from database.") -end -function OPSGROUP:_CheckInZones() -if self.checkzones and self:IsAlive()then -local Ncheck=self.checkzones:Count() -local Ninside=self.inzones:Count() -self:T(self.lid..string.format("Check if group is in %d zones. Currently it is in %d zones.",self.checkzones:Count(),self.inzones:Count())) -local leftzones={} -for inzonename,inzone in pairs(self.inzones:GetSet())do -local isstillinzone=self.group:IsInZone(inzone) -if not isstillinzone then -table.insert(leftzones,inzone) -end -end -for _,leftzone in pairs(leftzones)do -self:LeaveZone(leftzone) -end -local enterzones={} -for checkzonename,_checkzone in pairs(self.checkzones:GetSet())do -local checkzone=_checkzone -local isincheckzone=self.group:IsInZone(checkzone) -if isincheckzone and not self.inzones:_Find(checkzonename)then -table.insert(enterzones,checkzone) -end -end -for _,enterzone in pairs(enterzones)do -self:EnterZone(enterzone) -end -end -end -function OPSGROUP:_CheckDetectedUnits() -if self.group and not self:IsDead()then -local detectedtargets=self.group:GetDetectedTargets() -local detected={} -local groups={} -for DetectionObjectID,Detection in pairs(detectedtargets or{})do -local DetectedObject=Detection.object -if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then -local unit=UNIT:Find(DetectedObject) -if unit and unit:IsAlive()then -local unitname=unit:GetName() -table.insert(detected,unit) -self:DetectedUnit(unit) -local group=unit:GetGroup() -if group then -groups[group:GetName()]=group -end -end -end -end -for groupname,group in pairs(groups)do -self:DetectedGroup(group) -end -local lost={} -for _,_unit in pairs(self.detectedunits:GetSet())do -local unit=_unit -local gotit=false -for _,_du in pairs(detected)do -local du=_du -if unit:GetName()==du:GetName()then -gotit=true -end -end -if not gotit then -table.insert(lost,unit:GetName()) -self:DetectedUnitLost(unit) -end -end -self.detectedunits:RemoveUnitsByName(lost) -local lost={} -for _,_group in pairs(self.detectedgroups:GetSet())do -local group=_group -local gotit=false -for _,_du in pairs(groups)do -local du=_du -if group:GetName()==du:GetName()then -gotit=true -end -end -if not gotit then -table.insert(lost,group:GetName()) -self:DetectedGroupLost(group) -end -end -self.detectedgroups:RemoveGroupsByName(lost) -end -end -function OPSGROUP:_CheckGroupDone(delay) -if self:IsAlive()and self.isAI then -if delay and delay>0 then -self:ScheduleOnce(delay,self._CheckGroupDone,self) -else -if self:IsEngaging()then -self:UpdateRoute() -return -end -local waypoint=self:GetWaypoint(self.currentwp) -if waypoint then -local ntasks=self:CountTasksWaypoint(waypoint.uid) -if ntasks>0 then -self:T(self.lid..string.format("Still got %d tasks for the current waypoint UID=%d ==> RETURN (no action)",ntasks,waypoint.uid)) -return -end -end -if self.adinfinitum then -if#self.waypoints>0 then -local i=self:GetWaypointIndexNext(true) -local speed=self:GetSpeedToWaypoint(i) -self:UpdateRoute(i,speed) -self:T(self.lid..string.format("Adinfinitum=TRUE ==> Goto WP index=%d at speed=%d knots",i,speed)) -else -self:E(self.lid..string.format("WARNING: No waypoints left! Commanding a Full Stop")) -self:__FullStop(-1) -end -else -if self.passedfinalwp then -self:__FullStop(-1) -self:T(self.lid..string.format("Passed final WP, adinfinitum=FALSE ==> Full Stop")) -else -if#self.waypoints>0 then -self:T(self.lid..string.format("NOT Passed final WP, #WP>0 ==> Update Route")) -self:UpdateRoute() -else -self:E(self.lid..string.format("WARNING: No waypoints left! Commanding a Full Stop")) -self:__FullStop(-1) -end -end -end -end -end -end -function OPSGROUP:_CheckStuck() -if self:IsHolding()or self:Is("Rearming")then -return -end -local Tnow=timer.getTime() -local ExpectedSpeed=self:GetExpectedSpeed() -local speed=self:GetVelocity() -if speed<0.5 then -if ExpectedSpeed>0 and not self.stuckTimestamp then -self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected",speed,ExpectedSpeed)) -self.stuckTimestamp=Tnow -self.stuckVec3=self:GetVec3() -end -else -self.stuckTimestamp=nil -end -if self.stuckTimestamp then -local holdtime=Tnow-self.stuckTimestamp -if holdtime>=10*60 then -self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec",speed,ExpectedSpeed,holdtime)) -end -end -end -function OPSGROUP:_CheckDamage() -self.life=0 -local damaged=false -for _,_element in pairs(self.elements)do -local element=_element -local life=element.unit:GetLife() -self.life=self.life+life -if life0 then -local ammo=self:GetAmmoTot() -if self:IsRearming()then -if ammo.Total==self.ammo.Total then -self:Rearmed() -end -end -if self.outofAmmo and ammo.Total>0 then -self.outofAmmo=false -end -if ammo.Total==0 and not self.outofAmmo then -self.outofAmmo=true -self:OutOfAmmo() -end -if self.outofGuns and ammo.Guns>0 then -self.outoffGuns=false -end -if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then -self.outofGuns=true -self:OutOfGuns() -end -if self.outofRockets and ammo.Rockets>0 then -self.outoffRockets=false -end -if ammo.Rockets==0 and self.ammo.Rockets>0 and not self.outofRockets then -self.outofRockets=true -self:OutOfRockets() -end -if self.outofBombs and ammo.Bombs>0 then -self.outoffBombs=false -end -if ammo.Bombs==0 and self.ammo.Bombs>0 and not self.outofBombs then -self.outofBombs=true -self:OutOfBombs() -end -if self.outofMissiles and ammo.Missiles>0 then -self.outoffMissiles=false -end -if ammo.Missiles==0 and self.ammo.Missiles>0 and not self.outofMissiles then -self.outofMissiles=true -self:OutOfMissiles() -end -if self:IsEngaging()and ammo.Total==0 then -self:Disengage() -end -end -end -function OPSGROUP:_PrintTaskAndMissionStatus() -if self.verbose>=3 and#self.taskqueue>0 then -local text=string.format("Tasks #%d",#self.taskqueue) -for i,_task in pairs(self.taskqueue)do -local task=_task -local name=task.description -local taskid=task.dcstask.id or"unknown" -local status=task.status -local clock=UTILS.SecondsToClock(task.time,true) -local eta=task.time-timer.getAbsTime() -local started=task.timestamp and UTILS.SecondsToClock(task.timestamp,true)or"N/A" -local duration=-1 -if task.duration then -duration=task.duration -if task.timestamp then -duration=task.duration-(timer.getAbsTime()-task.timestamp) -else -duration=task.duration -end -end -if task.type==OPSGROUP.TaskType.SCHEDULED then -text=text..string.format("\n[%d] %s (%s): status=%s, scheduled=%s (%d sec), started=%s, duration=%d",i,taskid,name,status,clock,eta,started,duration) -elseif task.type==OPSGROUP.TaskType.WAYPOINT then -text=text..string.format("\n[%d] %s (%s): status=%s, waypoint=%d, started=%s, duration=%d, stopflag=%d",i,taskid,name,status,task.waypoint,started,duration,task.stopflag:Get()) -end -end -self:I(self.lid..text) -end -if self.verbose>=2 then -local Mission=self:GetMissionByID(self.currentmission) -local text=string.format("Missions %d, Current: %s",self:CountRemainingMissison(),Mission and Mission.name or"none") -for i,_mission in pairs(self.missionqueue)do -local mission=_mission -local Cstart=UTILS.SecondsToClock(mission.Tstart,true) -local Cstop=mission.Tstop and UTILS.SecondsToClock(mission.Tstop,true)or"INF" -text=text..string.format("\n[%d] %s (%s) status=%s (%s), Time=%s-%s, prio=%d wp=%s targets=%d", -i,tostring(mission.name),mission.type,mission:GetGroupStatus(self),tostring(mission.status),Cstart,Cstop,mission.prio,tostring(mission:GetGroupWaypointIndex(self)),mission:CountMissionTargets()) -end -self:I(self.lid..text) -end -end -function OPSGROUP:_CreateWaypoint(waypoint) -waypoint.uid=self.wpcounter -waypoint.npassed=0 -waypoint.coordinate=COORDINATE:New(waypoint.x,waypoint.alt,waypoint.y) -waypoint.name=string.format("Waypoint UID=%d",waypoint.uid) -waypoint.patrol=false -waypoint.detour=false -waypoint.astar=false -self.wpcounter=self.wpcounter+1 -return waypoint -end -function OPSGROUP:_AddWaypoint(waypoint,wpnumber) -wpnumber=wpnumber or#self.waypoints+1 -table.insert(self.waypoints,wpnumber,waypoint) -self:T(self.lid..string.format("Adding waypoint at index=%d id=%d",wpnumber,waypoint.uid)) -self.passedfinalwp=false -if self:IsHolding()then -self:Cruise() -end -end -function OPSGROUP:InitWaypoints() -self.waypoints0=self.group:GetTemplateRoutePoints() -self.waypoints={} -for index,wp in pairs(self.waypoints0)do -local coordinate=COORDINATE:New(wp.x,wp.alt,wp.y) -wp.speed=wp.speed or 0 -local speedknots=UTILS.MpsToKnots(wp.speed) -if index==1 then -self.speedWp=wp.speed -end -self:AddWaypoint(coordinate,speedknots,index-1,nil,false) -end -self:T(self.lid..string.format("Initializing %d waypoints",#self.waypoints)) -if#self.waypoints>0 then -if#self.waypoints==1 then -self.passedfinalwp=true -end -end -return self -end -function OPSGROUP:Route(waypoints,delay) -if delay and delay>0 then -self:ScheduleOnce(delay,OPSGROUP.Route,self,waypoints) -else -if self:IsAlive()then -local Tasks={} -local TaskRoute=self.group:TaskRoute(waypoints) -table.insert(Tasks,TaskRoute) -local TaskCombo=self.group:TaskCombo(Tasks) -if#Tasks>1 then -self:SetTask(TaskCombo) -else -self:SetTask(TaskRoute) -end -else -self:E(self.lid.."ERROR: Group is not alive! Cannot route group.") -end -end -return self -end -function OPSGROUP:_UpdateWaypointTasks(n) -local waypoints=self.waypoints or{} -local nwaypoints=#waypoints -for i,_wp in pairs(waypoints)do -local wp=_wp -if i>=n or nwaypoints==1 then -self:T2(self.lid..string.format("Updating waypoint task for waypoint %d/%d ID=%d. Last waypoint passed %d",i,nwaypoints,wp.uid,self.currentwp)) -local taskswp={} -local TaskPassingWaypoint=self.group:TaskFunction("OPSGROUP._PassingWaypoint",self,wp.uid) -table.insert(taskswp,TaskPassingWaypoint) -wp.task=self.group:TaskCombo(taskswp) -end -end -end -function OPSGROUP._PassingWaypoint(group,opsgroup,uid) -local waypoint=opsgroup:GetWaypointByID(uid) -if waypoint then -local currentwp=opsgroup.currentwp -opsgroup.currentwp=opsgroup:GetWaypointIndex(uid) -local wpnext=opsgroup:GetWaypointNext() -if wpnext then -if opsgroup.isGround then -opsgroup.formation=wpnext.action -end -opsgroup.speed=wpnext.speed -end -local text=string.format("Group passing waypoint uid=%d",uid) -opsgroup:T(opsgroup.lid..text) -if waypoint.astar then -opsgroup:RemoveWaypointByID(uid) -opsgroup:Cruise() -elseif waypoint.detour then -opsgroup:RemoveWaypointByID(uid) -if opsgroup:IsRearming()then -opsgroup:Rearming() -elseif opsgroup:IsRetreating()then -opsgroup:Retreated() -elseif opsgroup:IsEngaging()then -else -opsgroup:DetourReached() -if waypoint.detour==0 then -opsgroup:FullStop() -elseif waypoint.detour==1 then -opsgroup:Cruise() -else -opsgroup:E("ERROR: waypoint.detour should be 0 or 1") -end -end -else -if opsgroup.ispathfinding then -opsgroup.ispathfinding=false -end -waypoint.npassed=waypoint.npassed+1 -opsgroup:PassingWaypoint(waypoint) -end -end -end -function OPSGROUP._TaskExecute(group,opsgroup,task) -local text=string.format("_TaskExecute %s",task.description) -opsgroup:T3(opsgroup.lid..text) -if opsgroup then -opsgroup:TaskExecute(task) -end -end -function OPSGROUP._TaskDone(group,opsgroup,task) -local text=string.format("_TaskDone %s",task.description) -opsgroup:T3(opsgroup.lid..text) -if opsgroup then -opsgroup:TaskDone(task) -end -end -function OPSGROUP:SetDefaultROE(roe) -self.optionDefault.ROE=roe or ENUMS.ROE.ReturnFire -return self -end -function OPSGROUP:SwitchROE(roe) -if self:IsAlive()or self:IsInUtero()then -self.option.ROE=roe or self.optionDefault.ROE -if self:IsInUtero()then -self:T2(self.lid..string.format("Setting current ROE=%d when GROUP is SPAWNED",self.option.ROE)) -else -self.group:OptionROE(self.option.ROE) -self:T(self.lid..string.format("Setting current ROE=%d (%s)",self.option.ROE,self:_GetROEName(self.option.ROE))) -end -else -self:E(self.lid.."WARNING: Cannot switch ROE! Group is not alive") -end -return self -end -function OPSGROUP:_GetROEName(roe) -local name="unknown" -if roe==0 then -name="Weapon Free" -elseif roe==1 then -name="Open Fire/Weapon Free" -elseif roe==2 then -name="Open Fire" -elseif roe==3 then -name="Return Fire" -elseif roe==4 then -name="Weapon Hold" -end -return name -end -function OPSGROUP:GetROE() -return self.option.ROE or self.optionDefault.ROE -end -function OPSGROUP:SetDefaultROT(rot) -self.optionDefault.ROT=rot or ENUMS.ROT.PassiveDefense -return self -end -function OPSGROUP:SwitchROT(rot) -if self:IsAlive()or self:IsInUtero()then -self.option.ROT=rot or self.optionDefault.ROT -if self:IsInUtero()then -self:T2(self.lid..string.format("Setting current ROT=%d when GROUP is SPAWNED",self.option.ROT)) -else -self.group:OptionROT(self.option.ROT) -self:T(self.lid..string.format("Setting current ROT=%d (0=NoReaction, 1=Passive, 2=Evade, 3=ByPass, 4=AllowAbort)",self.option.ROT)) -end -else -self:E(self.lid.."WARNING: Cannot switch ROT! Group is not alive") -end -return self -end -function OPSGROUP:GetROT() -return self.option.ROT or self.optionDefault.ROT -end -function OPSGROUP:SetDefaultAlarmstate(alarmstate) -self.optionDefault.Alarm=alarmstate or 0 -return self -end -function OPSGROUP:SwitchAlarmstate(alarmstate) -if self:IsAlive()or self:IsInUtero()then -if self.isArmygroup or self.isNavygroup then -self.option.Alarm=alarmstate or self.optionDefault.Alarm -if self:IsInUtero()then -self:T2(self.lid..string.format("Setting current Alarm State=%d when GROUP is SPAWNED",self.option.Alarm)) -else -if self.option.Alarm==0 then -self.group:OptionAlarmStateAuto() -elseif self.option.Alarm==1 then -self.group:OptionAlarmStateGreen() -elseif self.option.Alarm==2 then -self.group:OptionAlarmStateRed() -else -self:E("ERROR: Unknown Alarm State! Setting to AUTO") -self.group:OptionAlarmStateAuto() -self.option.Alarm=0 -end -self:T(self.lid..string.format("Setting current Alarm State=%d (0=Auto, 1=Green, 2=Red)",self.option.Alarm)) -end -end -else -self:E(self.lid.."WARNING: Cannot switch Alarm State! Group is not alive.") -end -return self -end -function OPSGROUP:GetAlarmstate() -return self.option.Alarm or self.optionDefault.Alarm -end -function OPSGROUP:SetDefaultTACAN(Channel,Morse,UnitName,Band,OffSwitch) -self.tacanDefault={} -self.tacanDefault.Channel=Channel or 74 -self.tacanDefault.Morse=Morse or"XXX" -self.tacanDefault.BeaconName=UnitName -if self.isAircraft then -Band=Band or"Y" -else -Band=Band or"X" -end -self.tacanDefault.Band=Band -if OffSwitch then -self.tacanDefault.On=false -else -self.tacanDefault.On=true -end -return self -end -function OPSGROUP:_SwitchTACAN(Tacan) -if Tacan then -self:SwitchTACAN(Tacan.Channel,Tacan.Morse,Tacan.BeaconName,Tacan.Band) -else -if self.tacanDefault.On then -self:SwitchTACAN() -else -self:TurnOffTACAN() -end -end -end -function OPSGROUP:SwitchTACAN(Channel,Morse,UnitName,Band) -if self:IsInUtero()then -self:T(self.lid..string.format("Switching TACAN to DEFAULT when group is spawned")) -self:SetDefaultTACAN(Channel,Morse,UnitName,Band) -elseif self:IsAlive()then -Channel=Channel or self.tacanDefault.Channel -Morse=Morse or self.tacanDefault.Morse -Band=Band or self.tacanDefault.Band -UnitName=UnitName or self.tacanDefault.BeaconName -local unit=self:GetUnit(1) -if UnitName then -if type(UnitName)=="number"then -unit=self.group:GetUnit(UnitName) -else -unit=UNIT:FindByName(UnitName) -end -end -if not unit then -self:T(self.lid.."WARNING: Could not get TACAN unit. Trying first unit in the group") -unit=self:GetUnit(1) -end -if unit and unit:IsAlive()then -local UnitID=unit:GetID() -local Type=BEACON.Type.TACAN -local System=BEACON.System.TACAN -if self.isAircraft then -System=BEACON.System.TACAN_TANKER_Y -end -local Frequency=UTILS.TACANToFrequency(Channel,Band) -unit:CommandActivateBeacon(Type,System,Frequency,UnitID,Channel,Band,true,Morse,true) -self.tacan.Channel=Channel -self.tacan.Morse=Morse -self.tacan.Band=Band -self.tacan.BeaconName=unit:GetName() -self.tacan.BeaconUnit=unit -self.tacan.On=true -self:T(self.lid..string.format("Switching TACAN to Channel %d%s Morse %s on unit %s",self.tacan.Channel,self.tacan.Band,tostring(self.tacan.Morse),self.tacan.BeaconName)) -else -self:E(self.lid.."ERROR: Cound not set TACAN! Unit is not alive") -end -else -self:E(self.lid.."ERROR: Cound not set TACAN! Group is not alive and not in utero any more") -end -return self -end -function OPSGROUP:TurnOffTACAN() -if self.tacan.BeaconUnit and self.tacan.BeaconUnit:IsAlive()then -self.tacan.BeaconUnit:CommandDeactivateBeacon() -end -self:T(self.lid..string.format("Switching TACAN OFF")) -self.tacan.On=false -end -function OPSGROUP:GetTACAN() -return self.tacan.Channel,self.tacan.Morse,self.tacan.Band,self.tacan.On,self.tacan.BeaconName -end -function OPSGROUP:SetDefaultICLS(Channel,Morse,UnitName,OffSwitch) -self.iclsDefault={} -self.iclsDefault.Channel=Channel or 1 -self.iclsDefault.Morse=Morse or"XXX" -self.iclsDefault.BeaconName=UnitName -if OffSwitch then -self.iclsDefault.On=false -else -self.iclsDefault.On=true -end -return self -end -function OPSGROUP:_SwitchICLS(Icls) -if Icls then -self:SwitchICLS(Icls.Channel,Icls.Morse,Icls.BeaconName) -else -if self.iclsDefault.On then -self:SwitchICLS() -else -self:TurnOffICLS() -end -end -end -function OPSGROUP:SwitchICLS(Channel,Morse,UnitName) -if self:IsInUtero()then -self:SetDefaultICLS(Channel,Morse,UnitName) -self:T2(self.lid..string.format("Switching ICLS to Channel %d Morse %s on unit %s when GROUP is SPAWNED",self.iclsDefault.Channel,tostring(self.iclsDefault.Morse),tostring(self.iclsDefault.BeaconName))) -elseif self:IsAlive()then -Channel=Channel or self.iclsDefault.Channel -Morse=Morse or self.iclsDefault.Morse -local unit=self:GetUnit(1) -if UnitName then -if type(UnitName)=="number"then -unit=self:GetUnit(UnitName) -else -unit=UNIT:FindByName(UnitName) -end -end -if not unit then -self:T(self.lid.."WARNING: Could not get ICLS unit. Trying first unit in the group") -unit=self:GetUnit(1) -end -if unit and unit:IsAlive()then -local UnitID=unit:GetID() -unit:CommandActivateICLS(Channel,UnitID,Morse) -self.icls.Channel=Channel -self.icls.Morse=Morse -self.icls.Band=nil -self.icls.BeaconName=unit:GetName() -self.icls.BeaconUnit=unit -self.icls.On=true -self:T(self.lid..string.format("Switching ICLS to Channel %d Morse %s on unit %s",self.icls.Channel,tostring(self.icls.Morse),self.icls.BeaconName)) -else -self:E(self.lid.."ERROR: Cound not set ICLS! Unit is not alive.") -end -end -return self -end -function OPSGROUP:TurnOffICLS() -if self.icls.BeaconUnit and self.icls.BeaconUnit:IsAlive()then -self.icls.BeaconUnit:CommandDeactivateICLS() -end -self:T(self.lid..string.format("Switching ICLS OFF")) -self.icls.On=false -end -function OPSGROUP:SetDefaultRadio(Frequency,Modulation,OffSwitch) -self.radioDefault={} -self.radioDefault.Freq=Frequency or 251 -self.radioDefault.Modu=Modulation or radio.modulation.AM -if OffSwitch then -self.radioDefault.On=false -else -self.radioDefault.On=true -end -return self -end -function OPSGROUP:GetRadio() -return self.radio.Freq,self.radio.Modu,self.radio.On -end -function OPSGROUP:SwitchRadio(Frequency,Modulation) -if self:IsInUtero()then -self:SetDefaultRadio(Frequency,Modulation) -self:T2(self.lid..string.format("Switching radio to frequency %.3f MHz %s when GROUP is SPAWNED",self.radioDefault.Freq,UTILS.GetModulationName(self.radioDefault.Modu))) -elseif self:IsAlive()then -Frequency=Frequency or self.radioDefault.Freq -Modulation=Modulation or self.radioDefault.Modu -if self.isAircraft and not self.radio.On then -self.group:SetOption(AI.Option.Air.id.SILENCE,false) -end -self.group:CommandSetFrequency(Frequency,Modulation) -self.radio.Freq=Frequency -self.radio.Modu=Modulation -self.radio.On=true -self:T(self.lid..string.format("Switching radio to frequency %.3f MHz %s",self.radio.Freq,UTILS.GetModulationName(self.radio.Modu))) -else -self:E(self.lid.."ERROR: Cound not set Radio! Group is not alive or not in utero any more") -end -return self -end -function OPSGROUP:TurnOffRadio() -if self:IsAlive()then -if self.isAircraft then -self.group:SetOption(AI.Option.Air.id.SILENCE,true) -self.radio.On=false -self:T(self.lid..string.format("Switching radio OFF")) -else -self:E(self.lid.."ERROR: Radio can only be turned off for aircraft!") -end -end -return self -end -function OPSGROUP:SetDefaultFormation(Formation) -self.optionDefault.Formation=Formation -return self -end -function OPSGROUP:SwitchFormation(Formation) -if self:IsAlive()then -Formation=Formation or self.optionDefault.Formation -if self.isAircraft then -self.group:SetOption(AI.Option.Air.id.FORMATION,Formation) -elseif self.isGround then -else -self:E(self.lid.."ERROR: Formation can only be set for aircraft or ground units!") -return self -end -self.option.Formation=Formation -self:T(self.lid..string.format("Switching formation to %d",self.option.Formation)) -end -return self -end -function OPSGROUP:SetDefaultCallsign(CallsignName,CallsignNumber) -self.callsignDefault={} -self.callsignDefault.NumberSquad=CallsignName -self.callsignDefault.NumberGroup=CallsignNumber or 1 -return self -end -function OPSGROUP:SwitchCallsign(CallsignName,CallsignNumber) -if self:IsInUtero()then -self:SetDefaultCallsign(CallsignName,CallsignNumber) -elseif self:IsAlive()then -CallsignName=CallsignName or self.callsignDefault.NumberSquad -CallsignNumber=CallsignNumber or self.callsignDefault.NumberGroup -self.callsign.NumberSquad=CallsignName -self.callsign.NumberGroup=CallsignNumber -self:T(self.lid..string.format("Switching callsign to %d-%d",self.callsign.NumberSquad,self.callsign.NumberGroup)) -self.group:CommandSetCallsign(self.callsign.NumberSquad,self.callsign.NumberGroup) -else -end -return self -end -function OPSGROUP:_UpdatePosition() -if self:IsAlive()then -self.positionLast=self.position or self:GetVec3() -self.headingLast=self.heading or self:GetHeading() -self.orientXLast=self.orientX or self:GetOrientationX() -self.velocityLast=self.velocity or self.group:GetVelocityMPS() -self.position=self:GetVec3() -self.heading=self:GetHeading() -self.orientX=self:GetOrientationX() -self.velocity=self:GetVelocity() -local Tnow=timer.getTime() -self.dTpositionUpdate=self.TpositionUpdate and Tnow-self.TpositionUpdate or 0 -self.TpositionUpdate=Tnow -if not self.traveldist then -self.traveldist=0 -end -self.travelds=UTILS.VecNorm(UTILS.VecSubstract(self.position,self.positionLast)) -self.traveldist=self.traveldist+self.travelds -end -return self -end -function OPSGROUP:_AllSameStatus(status) -for _,_element in pairs(self.elements)do -local element=_element -if element.status==OPSGROUP.ElementStatus.DEAD then -elseif element.status~=status then -return false -end -end -return true -end -function OPSGROUP:_AllSimilarStatus(status) -if status==OPSGROUP.ElementStatus.DEAD then -for _,_element in pairs(self.elements)do -local element=_element -if element.status~=OPSGROUP.ElementStatus.DEAD then -return false -end -end -return true -end -for _,_element in pairs(self.elements)do -local element=_element -self:T2(self.lid..string.format("Status=%s, element %s status=%s",status,element.name,element.status)) -if element.status~=OPSGROUP.ElementStatus.DEAD then -if status==OPSGROUP.ElementStatus.SPAWNED then -if element.status~=status and -element.status==OPSGROUP.ElementStatus.INUTERO then -return false -end -elseif status==OPSGROUP.ElementStatus.PARKING then -if element.status~=status or -(element.status==OPSGROUP.ElementStatus.INUTERO or -element.status==OPSGROUP.ElementStatus.SPAWNED)then -return false -end -elseif status==OPSGROUP.ElementStatus.ENGINEON then -if element.status~=status and -(element.status==OPSGROUP.ElementStatus.INUTERO or -element.status==OPSGROUP.ElementStatus.SPAWNED or -element.status==OPSGROUP.ElementStatus.PARKING)then -return false -end -elseif status==OPSGROUP.ElementStatus.TAXIING then -if element.status~=status and -(element.status==OPSGROUP.ElementStatus.INUTERO or -element.status==OPSGROUP.ElementStatus.SPAWNED or -element.status==OPSGROUP.ElementStatus.PARKING or -element.status==OPSGROUP.ElementStatus.ENGINEON)then -return false -end -elseif status==OPSGROUP.ElementStatus.TAKEOFF then -if element.status~=status and -(element.status==OPSGROUP.ElementStatus.INUTERO or -element.status==OPSGROUP.ElementStatus.SPAWNED or -element.status==OPSGROUP.ElementStatus.PARKING or -element.status==OPSGROUP.ElementStatus.ENGINEON or -element.status==OPSGROUP.ElementStatus.TAXIING)then -return false -end -elseif status==OPSGROUP.ElementStatus.AIRBORNE then -if element.status~=status and -(element.status==OPSGROUP.ElementStatus.INUTERO or -element.status==OPSGROUP.ElementStatus.SPAWNED or -element.status==OPSGROUP.ElementStatus.PARKING or -element.status==OPSGROUP.ElementStatus.ENGINEON or -element.status==OPSGROUP.ElementStatus.TAXIING or -element.status==OPSGROUP.ElementStatus.TAKEOFF)then -return false -end -elseif status==OPSGROUP.ElementStatus.LANDED then -if element.status~=status and -(element.status==OPSGROUP.ElementStatus.AIRBORNE or -element.status==OPSGROUP.ElementStatus.LANDING)then -return false -end -elseif status==OPSGROUP.ElementStatus.ARRIVED then -if element.status~=status and -(element.status==OPSGROUP.ElementStatus.AIRBORNE or -element.status==OPSGROUP.ElementStatus.LANDING or -element.status==OPSGROUP.ElementStatus.LANDED)then -return false -end -end -else -end -end -self:T2(self.lid..string.format("All %d elements have similar status %s ==> returning TRUE",#self.elements,status)) -return true -end -function OPSGROUP:_UpdateStatus(element,newstatus,airbase) -local oldstatus=element.status -element.status=newstatus -self:T3(self.lid..string.format("UpdateStatus element=%s: %s --> %s",element.name,oldstatus,newstatus)) -for _,_element in pairs(self.elements)do -local Element=_element -self:T3(self.lid..string.format("Element %s: %s",Element.name,Element.status)) -end -if newstatus==OPSGROUP.ElementStatus.SPAWNED then -if self:_AllSimilarStatus(newstatus)then -self:__Spawned(-0.5) -end -elseif newstatus==OPSGROUP.ElementStatus.PARKING then -if self:_AllSimilarStatus(newstatus)then -self:__Parking(-0.5) -end -elseif newstatus==OPSGROUP.ElementStatus.ENGINEON then -elseif newstatus==OPSGROUP.ElementStatus.TAXIING then -if self:_AllSimilarStatus(newstatus)then -self:__Taxiing(-0.5) -end -elseif newstatus==OPSGROUP.ElementStatus.TAKEOFF then -if self:_AllSimilarStatus(newstatus)then -self:__Takeoff(-0.5,airbase) -end -elseif newstatus==OPSGROUP.ElementStatus.AIRBORNE then -if self:_AllSimilarStatus(newstatus)then -self:__Airborne(-0.5) -end -elseif newstatus==OPSGROUP.ElementStatus.LANDED then -if self:_AllSimilarStatus(newstatus)then -if self:IsLandingAt()then -self:LandedAt() -else -self:Landed(airbase) -end -end -elseif newstatus==OPSGROUP.ElementStatus.ARRIVED then -if self:_AllSimilarStatus(newstatus)then -if self:IsLanded()then -self:Arrived() -elseif self:IsAirborne()then -self:Landed() -self:Arrived() -end -end -elseif newstatus==OPSGROUP.ElementStatus.DEAD then -if self:_AllSimilarStatus(newstatus)then -self:__Dead(-1) -end -end -end -function OPSGROUP:_SetElementStatusAll(status) -for _,_element in pairs(self.elements)do -local element=_element -if element.status~=OPSGROUP.ElementStatus.DEAD then -element.status=status -end -end -end -function OPSGROUP:GetElementByName(unitname) -for _,_element in pairs(self.elements)do -local element=_element -if element.name==unitname then -return element -end -end -return nil -end -function OPSGROUP:GetElementAlive() -for _,_element in pairs(self.elements)do -local element=_element -if element.status~=OPSGROUP.ElementStatus.DEAD then -if element.unit and element.unit:IsAlive()then -return element -end -end -end -return nil -end -function OPSGROUP:GetNelements(status) -local n=0 -for _,_element in pairs(self.elements)do -local element=_element -if element.status~=OPSGROUP.ElementStatus.DEAD then -if element.unit and element.unit:IsAlive()then -if status==nil or element.status==status then -n=n+1 -end -end -end -end -return n -end -function OPSGROUP:GetAmmoElement(element) -return self:GetAmmoUnit(element.unit) -end -function OPSGROUP:GetAmmoTot() -local units=self.group:GetUnits() -local Ammo={} -Ammo.Total=0 -Ammo.Guns=0 -Ammo.Rockets=0 -Ammo.Bombs=0 -Ammo.Torpedos=0 -Ammo.Missiles=0 -Ammo.MissilesAA=0 -Ammo.MissilesAG=0 -Ammo.MissilesAS=0 -Ammo.MissilesCR=0 -Ammo.MissilesSA=0 -for _,_unit in pairs(units)do -local unit=_unit -if unit and unit:IsAlive()~=nil then -local ammo=self:GetAmmoUnit(unit) -Ammo.Total=Ammo.Total+ammo.Total -Ammo.Guns=Ammo.Guns+ammo.Guns -Ammo.Rockets=Ammo.Rockets+ammo.Rockets -Ammo.Bombs=Ammo.Bombs+ammo.Bombs -Ammo.Torpedos=Ammo.Torpedos+ammo.Torpedos -Ammo.Missiles=Ammo.Missiles+ammo.Missiles -Ammo.MissilesAA=Ammo.MissilesAA+ammo.MissilesAA -Ammo.MissilesAG=Ammo.MissilesAG+ammo.MissilesAG -Ammo.MissilesAS=Ammo.MissilesAS+ammo.MissilesAS -Ammo.MissilesCR=Ammo.MissilesCR+ammo.MissilesCR -Ammo.MissilesSA=Ammo.MissilesSA+ammo.MissilesSA -end -end -return Ammo -end -function OPSGROUP:GetAmmoUnit(unit,display) -if display==nil then -display=false -end -local nammo=0 -local nshells=0 -local nrockets=0 -local nmissiles=0 -local nmissilesAA=0 -local nmissilesAG=0 -local nmissilesAS=0 -local nmissilesSA=0 -local nmissilesBM=0 -local nmissilesCR=0 -local ntorps=0 -local nbombs=0 -local text=string.format("OPSGROUP group %s - unit %s:\n",self.groupname,unit:GetName()) -local ammotable=unit:GetAmmo() -if ammotable then -local weapons=#ammotable -for w=1,weapons do -local Nammo=ammotable[w]["count"] -local Tammo=ammotable[w]["desc"]["typeName"] -local _weaponString=UTILS.Split(Tammo,"%.") -local _weaponName=_weaponString[#_weaponString] -local Category=ammotable[w].desc.category -local MissileCategory=nil -if Category==Weapon.Category.MISSILE then -MissileCategory=ammotable[w].desc.missileCategory -end -if Category==Weapon.Category.SHELL then -nshells=nshells+Nammo -text=text..string.format("- %d shells of type %s\n",Nammo,_weaponName) -elseif Category==Weapon.Category.ROCKET then -nrockets=nrockets+Nammo -text=text..string.format("- %d rockets of type %s\n",Nammo,_weaponName) -elseif Category==Weapon.Category.BOMB then -nbombs=nbombs+Nammo -text=text..string.format("- %d bombs of type %s\n",Nammo,_weaponName) -elseif Category==Weapon.Category.MISSILE then -if MissileCategory==Weapon.MissileCategory.AAM then -nmissiles=nmissiles+Nammo -nmissilesAA=nmissilesAA+Nammo -elseif MissileCategory==Weapon.MissileCategory.SAM then -nmissiles=nmissiles+Nammo -nmissilesSA=nmissilesSA+Nammo -elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then -nmissiles=nmissiles+Nammo -nmissilesAS=nmissilesAS+Nammo -elseif MissileCategory==Weapon.MissileCategory.BM then -nmissiles=nmissiles+Nammo -nmissilesAG=nmissilesAG+Nammo -elseif MissileCategory==Weapon.MissileCategory.CRUISE then -nmissiles=nmissiles+Nammo -nmissilesCR=nmissilesCR+Nammo -elseif MissileCategory==Weapon.MissileCategory.OTHER then -nmissiles=nmissiles+Nammo -nmissilesAG=nmissilesAG+Nammo -end -text=text..string.format("- %d %s missiles of type %s\n",Nammo,self:_MissileCategoryName(MissileCategory),_weaponName) -elseif Category==Weapon.Category.TORPEDO then -ntorps=ntorps+Nammo -text=text..string.format("- %d torpedos of type %s\n",Nammo,_weaponName) -else -text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n",Nammo,Tammo,Category,tostring(MissileCategory)) -end -end -end -if display then -self:I(self.lid..text) -else -self:T3(self.lid..text) -end -nammo=nshells+nrockets+nmissiles+nbombs+ntorps -local ammo={} -ammo.Total=nammo -ammo.Guns=nshells -ammo.Rockets=nrockets -ammo.Bombs=nbombs -ammo.Torpedos=ntorps -ammo.Missiles=nmissiles -ammo.MissilesAA=nmissilesAA -ammo.MissilesAG=nmissilesAG -ammo.MissilesAS=nmissilesAS -ammo.MissilesCR=nmissilesCR -ammo.MissilesBM=nmissilesBM -ammo.MissilesSA=nmissilesSA -return ammo -end -function OPSGROUP:_MissileCategoryName(categorynumber) -local cat="unknown" -if categorynumber==Weapon.MissileCategory.AAM then -cat="air-to-air" -elseif categorynumber==Weapon.MissileCategory.SAM then -cat="surface-to-air" -elseif categorynumber==Weapon.MissileCategory.BM then -cat="ballistic" -elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then -cat="anti-ship" -elseif categorynumber==Weapon.MissileCategory.CRUISE then -cat="cruise" -elseif categorynumber==Weapon.MissileCategory.OTHER then -cat="other" -end -return cat -end -function OPSGROUP:_CoordinateFromObject(Object) -if Object:IsInstanceOf("COORDINATE")then -return Object -else -if Object:IsInstanceOf("POSITIONABLE")or Object:IsInstanceOf("ZONE_BASE")then -self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate") -return Object:GetCoordinate() -else -self:E(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!") -end -end -return nil -end -FLIGHTGROUP={ -ClassName="FLIGHTGROUP", -homebase=nil, -destbase=nil, -homezone=nil, -destzone=nil, -actype=nil, -speedMax=nil, -rangemax=nil, -ceiling=nil, -fuellow=false, -fuellowthresh=nil, -fuellowrtb=nil, -fuelcritical=nil, -fuelcriticalthresh=nil, -fuelcriticalrtb=false, -outofAAMrtb=true, -outofAGMrtb=true, -squadron=nil, -flightcontrol=nil, -flaghold=nil, -Tholding=nil, -Tparking=nil, -menu=nil, -ishelo=nil, -RTBRecallCount=0, -} -FLIGHTGROUP.Attribute={ -TRANSPORTPLANE="TransportPlane", -AWACS="AWACS", -FIGHTER="Fighter", -BOMBER="Bomber", -TANKER="Tanker", -TRANSPORTHELO="TransportHelo", -ATTACKHELO="AttackHelo", -UAV="UAV", -OTHER="Other", -} -FLIGHTGROUP.version="0.6.1" -function FLIGHTGROUP:New(group) -local fg=_DATABASE:GetFlightGroup(group) -if fg then -fg:I(fg.lid..string.format("WARNING: Flight group already exists in data base!")) -return fg -end -local self=BASE:Inherit(self,OPSGROUP:New(group)) -self.lid=string.format("FLIGHTGROUP %s | ",self.groupname) -self:SetFuelLowThreshold() -self:SetFuelLowRTB() -self:SetFuelCriticalThreshold() -self:SetFuelCriticalRTB() -self:SetDefaultROE() -self:SetDefaultROT() -self:SetDetection() -self.isFlightgroup=true -self.flaghold=USERFLAG:New(string.format("%s_FlagHold",self.groupname)) -self.flaghold:Set(0) -self:AddTransition("*","RTB","Inbound") -self:AddTransition("*","RTZ","Inbound") -self:AddTransition("Inbound","Holding","Holding") -self:AddTransition("*","Refuel","Going4Fuel") -self:AddTransition("Going4Fuel","Refueled","Airborne") -self:AddTransition("*","LandAt","LandingAt") -self:AddTransition("LandingAt","LandedAt","LandedAt") -self:AddTransition("*","Wait","*") -self:AddTransition("*","FuelLow","*") -self:AddTransition("*","FuelCritical","*") -self:AddTransition("*","OutOfMissilesAA","*") -self:AddTransition("*","OutOfMissilesAG","*") -self:AddTransition("*","OutOfMissilesAS","*") -self:AddTransition("Airborne","EngageTarget","Engaging") -self:AddTransition("Engaging","Disengage","Airborne") -self:AddTransition("*","ElementParking","*") -self:AddTransition("*","ElementEngineOn","*") -self:AddTransition("*","ElementTaxiing","*") -self:AddTransition("*","ElementTakeoff","*") -self:AddTransition("*","ElementAirborne","*") -self:AddTransition("*","ElementLanded","*") -self:AddTransition("*","ElementArrived","*") -self:AddTransition("*","ElementOutOfAmmo","*") -self:AddTransition("*","Parking","Parking") -self:AddTransition("*","Taxiing","Taxiing") -self:AddTransition("*","Takeoff","Airborne") -self:AddTransition("*","Airborne","Airborne") -self:AddTransition("*","Landing","Landing") -self:AddTransition("*","Landed","Landed") -self:AddTransition("*","Arrived","Arrived") -if false then -BASE:TraceOnOff(true) -BASE:TraceClass(self.ClassName) -BASE:TraceLevel(1) -end -_DATABASE:AddFlightGroup(self) -self:HandleEvent(EVENTS.Birth,self.OnEventBirth) -self:HandleEvent(EVENTS.EngineStartup,self.OnEventEngineStartup) -self:HandleEvent(EVENTS.Takeoff,self.OnEventTakeOff) -self:HandleEvent(EVENTS.Land,self.OnEventLanding) -self:HandleEvent(EVENTS.EngineShutdown,self.OnEventEngineShutdown) -self:HandleEvent(EVENTS.PilotDead,self.OnEventPilotDead) -self:HandleEvent(EVENTS.Ejection,self.OnEventEjection) -self:HandleEvent(EVENTS.Crash,self.OnEventCrash) -self:HandleEvent(EVENTS.RemoveUnit,self.OnEventRemoveUnit) -self:HandleEvent(EVENTS.UnitLost,self.OnEventUnitLost) -self:HandleEvent(EVENTS.Kill,self.OnEventKill) -self:InitWaypoints() -self:_InitGroup() -self:__Status(-1) -self.timerQueueUpdate=TIMER:New(self._QueueUpdate,self):Start(2,5) -self.timerCheckZone=TIMER:New(self._CheckInZones,self):Start(3,10) -return self -end -function FLIGHTGROUP:AddTaskEnrouteEngageTargetsInZone(ZoneRadius,TargetTypes,Priority) -local Task=self.group:EnRouteTaskEngageTargetsInZone(ZoneRadius:GetVec2(),ZoneRadius:GetRadius(),TargetTypes,Priority) -self:AddTaskEnroute(Task) -end -function FLIGHTGROUP:SetAirwing(airwing) -self:T(self.lid..string.format("Add flight to AIRWING %s",airwing.alias)) -self.airwing=airwing -return self -end -function FLIGHTGROUP:GetAirWing() -return self.airwing -end -function FLIGHTGROUP:SetFlightControl(flightcontrol) -if self.flightcontrol then -if self.flightcontrol.airbasename==flightcontrol.airbasename then -return -else -self.flightcontrol:_RemoveFlight(self) -end -end -self:I(self.lid..string.format("Setting FLIGHTCONTROL to airbase %s",flightcontrol.airbasename)) -self.flightcontrol=flightcontrol -table.insert(flightcontrol.flights,self) -if self.isAI==false then -self:_UpdateMenu(0.5) -end -return self -end -function FLIGHTGROUP:GetFlightControl() -return self.flightcontrol -end -function FLIGHTGROUP:SetHomebase(HomeAirbase) -self.homebase=HomeAirbase -return self -end -function FLIGHTGROUP:SetDestinationbase(DestinationAirbase) -self.destbase=DestinationAirbase -return self -end -function FLIGHTGROUP:SetAirboss(airboss) -self.airboss=airboss -return self -end -function FLIGHTGROUP:SetFuelLowThreshold(threshold) -self.fuellowthresh=threshold or 25 -return self -end -function FLIGHTGROUP:SetFuelLowRTB(switch) -if switch==false then -self.fuellowrtb=false -else -self.fuellowrtb=true -end -return self -end -function FLIGHTGROUP:SetOutOfAAMRTB(switch) -if switch==false then -self.outofAAMrtb=false -else -self.outofAAMrtb=true -end -return self -end -function FLIGHTGROUP:SetOutOfAGMRTB(switch) -if switch==false then -self.outofAGMrtb=false -else -self.outofAGMrtb=true -end -return self -end -function FLIGHTGROUP:SetFuelLowRefuel(switch) -if switch==false then -self.fuellowrefuel=false -else -self.fuellowrefuel=true -end -return self -end -function FLIGHTGROUP:SetFuelCriticalThreshold(threshold) -self.fuelcriticalthresh=threshold or 10 -return self -end -function FLIGHTGROUP:SetFuelCriticalRTB(switch) -if switch==false then -self.fuelcriticalrtb=false -else -self.fuelcriticalrtb=true -end -return self -end -function FLIGHTGROUP:SetEngageDetectedOn(RangeMax,TargetTypes,EngageZoneSet,NoEngageZoneSet) -if TargetTypes then -if type(TargetTypes)~="table"then -TargetTypes={TargetTypes} -end -else -TargetTypes={"All"} -end -if EngageZoneSet and EngageZoneSet:IsInstanceOf("ZONE_BASE")then -local zoneset=SET_ZONE:New():AddZone(EngageZoneSet) -EngageZoneSet=zoneset -end -if NoEngageZoneSet and NoEngageZoneSet:IsInstanceOf("ZONE_BASE")then -local zoneset=SET_ZONE:New():AddZone(NoEngageZoneSet) -NoEngageZoneSet=zoneset -end -self.engagedetectedOn=true -self.engagedetectedRmax=UTILS.NMToMeters(RangeMax or 25) -self.engagedetectedTypes=TargetTypes -self.engagedetectedEngageZones=EngageZoneSet -self.engagedetectedNoEngageZones=NoEngageZoneSet -self:SetDetection(true) -return self -end -function FLIGHTGROUP:SetEngageDetectedOff() -self.engagedetectedOn=false -return self -end -function FLIGHTGROUP:SetDespawnAfterLanding() -self.despawnAfterLanding=true -return self -end -function FLIGHTGROUP:IsParking() -return self:Is("Parking") -end -function FLIGHTGROUP:IsTaxiing() -return self:Is("Taxiing") -end -function FLIGHTGROUP:IsAirborne() -return self:Is("Airborne") -end -function FLIGHTGROUP:IsWaiting() -return self:Is("Waiting") -end -function FLIGHTGROUP:IsLanding() -return self:Is("Landing") -end -function FLIGHTGROUP:IsLanded() -return self:Is("Landed") -end -function FLIGHTGROUP:IsArrived() -return self:Is("Arrived") -end -function FLIGHTGROUP:IsInbound() -return self:Is("Inbound") -end -function FLIGHTGROUP:IsHolding() -return self:Is("Holding") -end -function FLIGHTGROUP:IsGoing4Fuel() -return self:Is("Going4Fuel") -end -function FLIGHTGROUP:IsLandingAt() -return self:Is("LandingAt") -end -function FLIGHTGROUP:IsLandedAt() -return self:Is("LandedAt") -end -function FLIGHTGROUP:IsFuelLow() -return self.fuellow -end -function FLIGHTGROUP:IsFuelCritical() -return self.fuelcritical -end -function FLIGHTGROUP:CanAirToGround(ExcludeGuns) -local ammo=self:GetAmmoTot() -if ExcludeGuns then -return ammo.MissilesAG+ammo.Rockets+ammo.Bombs>0 -else -return ammo.MissilesAG+ammo.Rockets+ammo.Bombs+ammo.Guns>0 -end -end -function FLIGHTGROUP:CanAirToAir(ExcludeGuns) -local ammo=self:GetAmmoTot() -if ExcludeGuns then -return ammo.MissilesAA>0 -else -return ammo.MissilesAA+ammo.Guns>0 -end -end -function FLIGHTGROUP:StartUncontrolled(delay) -if delay and delay>0 then -self:T2(self.lid..string.format("Starting uncontrolled group in %d seconds",delay)) -self:ScheduleOnce(delay,FLIGHTGROUP.StartUncontrolled,self) -else -if self:IsAlive()then -self:T(self.lid.."Starting uncontrolled group") -self.group:StartUncontrolled(delay) -self.isUncontrolled=true -else -self:E(self.lid.."ERROR: Could not start uncontrolled group as it is NOT alive!") -end -end -return self -end -function FLIGHTGROUP:ClearToLand(Delay) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,FLIGHTGROUP.ClearToLand,self) -else -if self:IsHolding()then -self:T(self.lid..string.format("Clear to land ==> setting holding flag to 1 (true)")) -self.flaghold:Set(1) -end -end -return self -end -function FLIGHTGROUP:GetFuelMin() -local fuelmin=math.huge -for i,_element in pairs(self.elements)do -local element=_element -local unit=element.unit -local life=unit:GetLife() -if unit and unit:IsAlive()and life>1 then -local fuel=unit:GetFuel() -if fuel false")) -return false -elseif self:IsStopped()then -self:T(self.lid..string.format("Onbefore Status STOPPED ==> false")) -return false -end -return true -end -function FLIGHTGROUP:onafterStatus(From,Event,To) -local fsmstate=self:GetState() -self:_UpdatePosition() -if self.detectionOn then -self:_CheckDetectedUnits() -end -if self:IsParking()then -for _,_element in pairs(self.elements)do -local element=_element -if element.parking then -local dist=element.unit:GetCoordinate():Get2DDistance(element.parking.Coordinate) -if dist>10 then -if element.status==OPSGROUP.ElementStatus.ENGINEON then -self:ElementTaxiing(element) -end -end -else -end -end -end -if self.verbose>=1 then -local nTaskTot,nTaskSched,nTaskWP=self:CountRemainingTasks() -local nMissions=self:CountRemainingMissison() -local text=string.format("Status %s [%d/%d]: Tasks=%d (%d,%d) Curr=%d, Missions=%s, Waypoint=%d/%d, Detected=%d, Home=%s, Destination=%s", -fsmstate,#self.elements,#self.elements,nTaskTot,nTaskSched,nTaskWP,self.taskcurrent,nMissions,self.currentwp or 0,self.waypoints and#self.waypoints or 0, -self.detectedunits:Count(),self.homebase and self.homebase:GetName()or"unknown",self.destbase and self.destbase:GetName()or"unknown") -self:I(self.lid..text) -end -if self.verbose>=2 then -local text="Elements:" -for i,_element in pairs(self.elements)do -local element=_element -local name=element.name -local status=element.status -local unit=element.unit -local fuel=unit:GetFuel()or 0 -local life=unit:GetLifeRelative()or 0 -local parking=element.parking and tostring(element.parking.TerminalID)or"X" -local ammo=self:GetAmmoElement(element) -text=text..string.format("\n[%d] %s: status=%s, fuel=%.1f, life=%.1f, guns=%d, rockets=%d, bombs=%d, missiles=%d (AA=%d, AG=%d, AS=%s), parking=%s", -i,name,status,fuel*100,life*100,ammo.Guns,ammo.Rockets,ammo.Bombs,ammo.Missiles,ammo.MissilesAA,ammo.MissilesAG,ammo.MissilesAS,parking) -end -if#self.elements==0 then -text=text.." none!" -end -self:I(self.lid..text) -end -if self.verbose>=4 and self:IsAlive()then -local ds=self.travelds -local dt=self.dTpositionUpdate -local v=ds/dt -local TmaxFuel=math.huge -for _,_element in pairs(self.elements)do -local element=_element -local fuel=element.unit:GetFuel()or 0 -local dFrel=element.fuelrel-fuel -local dFreldt=dFrel/dt -local Tfuel=fuel/dFreldt -if Tfuel Tfuel=%.1f min",element.name,fuel*100,dFrel*100,dFreldt*100*60,Tfuel/60)) -element.fuelrel=fuel -end -self:I(self.lid..string.format("Travelled ds=%.1f km dt=%.1f s ==> v=%.1f knots. Fuel left for %.1f min",self.traveldist/1000,dt,UTILS.MpsToKnots(v),TmaxFuel/60)) -end -self:_PrintTaskAndMissionStatus() -if self:IsAlive()and self.group:IsAirborne(true)then -local fuelmin=self:GetFuelMin() -if fuelmin>=self.fuellowthresh then -self.fuellow=false -end -if fuelmin>=self.fuelcriticalthresh then -self.fuelcritical=false -end -if fuelmin spawned",element.name,self.homebase and self.homebase:GetName()or"unknown")) -self:__ElementSpawned(0.0,element) -end -end -end -function FLIGHTGROUP:OnEventEngineStartup(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -if self:IsAirborne()or self:IsInbound()or self:IsHolding()then -else -self:T3(self.lid..string.format("EVENT: Element %s started engines ==> taxiing (if AI)",element.name)) -if self.isAI then -self:ElementEngineOn(element) -else -if element.ai then -self:ElementEngineOn(element) -end -end -end -end -end -end -function FLIGHTGROUP:OnEventTakeOff(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -self:T3(self.lid..string.format("EVENT: Element %s took off ==> airborne",element.name)) -self:ElementTakeoff(element,EventData.Place) -end -end -end -function FLIGHTGROUP:OnEventLanding(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -local airbase=EventData.Place -local airbasename="unknown" -if airbase then -airbasename=tostring(airbase:GetName()) -end -if element then -self:T3(self.lid..string.format("EVENT: Element %s landed at %s ==> landed",element.name,airbasename)) -self:ElementLanded(element,airbase) -end -end -end -function FLIGHTGROUP:OnEventEngineShutdown(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -if element.unit and element.unit:IsAlive()then -local airbase=self:GetClosestAirbase() -local parking=self:GetParkingSpot(element,10,airbase) -if airbase and parking then -self:ElementArrived(element,airbase,parking) -self:T3(self.lid..string.format("EVENT: Element %s shut down engines ==> arrived",element.name)) -else -self:T3(self.lid..string.format("EVENT: Element %s shut down engines but is not parking. Is it dead?",element.name)) -end -else -end -end -end -end -function FLIGHTGROUP:OnEventCrash(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element and element.status~=OPSGROUP.ElementStatus.DEAD then -self:T(self.lid..string.format("EVENT: Element %s crashed ==> destroyed",element.name)) -self:ElementDestroyed(element) -end -end -end -function FLIGHTGROUP:OnEventUnitLost(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -self:T2(self.lid..string.format("EVENT: Unit %s lost at t=%.3f",EventData.IniUnitName,timer.getTime())) -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element and element.status~=OPSGROUP.ElementStatus.DEAD then -self:T(self.lid..string.format("EVENT: Element %s unit lost ==> destroyed t=%.3f",element.name,timer.getTime())) -self:ElementDestroyed(element) -end -end -end -function FLIGHTGROUP:OnEventKill(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local targetname=tostring(EventData.TgtUnitName) -self:T2(self.lid..string.format("EVENT: Unit %s killed object %s!",tostring(EventData.IniUnitName),targetname)) -local target=UNIT:FindByName(targetname) -if not target then -target=STATIC:FindByName(targetname,false) -end -if target then -self:T(self.lid..string.format("EVENT: Unit %s killed unit/static %s!",tostring(EventData.IniUnitName),targetname)) -self.Nkills=self.Nkills+1 -local mission=self:GetMissionCurrent() -if mission then -mission.Nkills=mission.Nkills+1 -end -end -end -end -function FLIGHTGROUP:OnEventRemoveUnit(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -self:T3(self.lid..string.format("EVENT: Element %s removed ==> dead",element.name)) -self:ElementDead(element) -end -end -end -function FLIGHTGROUP:onafterElementSpawned(From,Event,To,Element) -self:T(self.lid..string.format("Element spawned %s",Element.name)) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.SPAWNED) -if Element.unit:InAir(true)then -self:__ElementAirborne(0.11,Element) -else -local spot=self:GetParkingSpot(Element,10) -if spot then -self:__ElementParking(0.11,Element,spot) -else -self:T(self.lid..string.format("Element spawned not in air but not on any parking spot.")) -self:__ElementParking(0.11,Element) -end -end -end -function FLIGHTGROUP:onafterElementParking(From,Event,To,Element,Spot) -self:T(self.lid..string.format("Element parking %s at spot %s",Element.name,Element.parking and tostring(Element.parking.TerminalID)or"N/A")) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.PARKING) -if Spot then -self:_SetElementParkingAt(Element,Spot) -end -if self:IsTakeoffCold()then -elseif self:IsTakeoffHot()then -self:__ElementEngineOn(0.5,Element) -elseif self:IsTakeoffRunway()then -self:__ElementEngineOn(0.5,Element) -end -end -function FLIGHTGROUP:onafterElementEngineOn(From,Event,To,Element) -self:T(self.lid..string.format("Element %s started engines",Element.name)) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.ENGINEON) -end -function FLIGHTGROUP:onafterElementTaxiing(From,Event,To,Element) -local TerminalID=Element.parking and tostring(Element.parking.TerminalID)or"N/A" -self:T(self.lid..string.format("Element taxiing %s. Parking spot %s is now free",Element.name,TerminalID)) -self:_SetElementParkingFree(Element) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.TAXIING) -end -function FLIGHTGROUP:onafterElementTakeoff(From,Event,To,Element,airbase) -self:T(self.lid..string.format("Element takeoff %s at %s airbase.",Element.name,airbase and airbase:GetName()or"unknown")) -if Element.parking then -self:_SetElementParkingFree(Element) -end -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.TAKEOFF,airbase) -self:__ElementAirborne(2,Element) -end -function FLIGHTGROUP:onafterElementAirborne(From,Event,To,Element) -self:T2(self.lid..string.format("Element airborne %s",Element.name)) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.AIRBORNE) -end -function FLIGHTGROUP:onafterElementLanded(From,Event,To,Element,airbase) -self:T2(self.lid..string.format("Element landed %s at %s airbase",Element.name,airbase and airbase:GetName()or"unknown")) -if self.despawnAfterLanding then -self:DespawnElement(Element) -else -if self.ishelo then -local Spot=self:GetParkingSpot(Element,10,airbase) -self:_SetElementParkingAt(Element,Spot) -end -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.LANDED,airbase) -end -end -function FLIGHTGROUP:onafterElementArrived(From,Event,To,Element,airbase,Parking) -self:T(self.lid..string.format("Element arrived %s at %s airbase using parking spot %d",Element.name,airbase and airbase:GetName()or"unknown",Parking and Parking.TerminalID or-99)) -self:_SetElementParkingAt(Element,Parking) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.ARRIVED) -end -function FLIGHTGROUP:onafterElementDestroyed(From,Event,To,Element) -self:GetParent(self).onafterElementDestroyed(self,From,Event,To,Element) -end -function FLIGHTGROUP:onafterElementDead(From,Event,To,Element) -self:GetParent(self).onafterElementDead(self,From,Event,To,Element) -if self.flightcontrol and Element.parking then -self.flightcontrol:SetParkingFree(Element.parking) -end -Element.parking=nil -end -function FLIGHTGROUP:onafterSpawned(From,Event,To) -self:T(self.lid..string.format("Flight spawned")) -self:_UpdatePosition() -if self.isAI then -self:SwitchROE(self.option.ROE) -self:SwitchROT(self.option.ROT) -self:SwitchFormation(self.option.Formation) -self:_SwitchTACAN() -if self.radioDefault then -self:SwitchRadio() -else -self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,self.radio.On) -end -if self.callsignDefault then -self:SwitchCallsign(self.callsignDefault.NumberSquad,self.callsignDefault.NumberGroup) -else -self:SetDefaultCallsign(self.callsign.NumberSquad,self.callsign.NumberGroup) -end -self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT,true) -self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB,true) -self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO,false) -self:__UpdateRoute(-0.5) -else -self:_UpdateMenu() -end -end -function FLIGHTGROUP:onafterParking(From,Event,To) -self:T(self.lid..string.format("Flight is parking")) -local airbase=self:GetClosestAirbase() -local airbasename=airbase:GetName()or"unknown" -self.Tparking=timer.getAbsTime() -local flightcontrol=_DATABASE:GetFlightControl(airbasename) -if flightcontrol then -self:SetFlightControl(flightcontrol) -if self.flightcontrol then -self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.PARKING) -if not self.isAI then -self:_UpdateMenu(0.5) -end -end -end -end -function FLIGHTGROUP:onafterTaxiing(From,Event,To) -self:T(self.lid..string.format("Flight is taxiing")) -self.Tparking=nil -local airbase=self:GetClosestAirbase() -if self.flightcontrol and airbase and self.flightcontrol.airbasename==airbase:GetName()then -if self.isAI then -self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.TAKEOFF) -else -self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.TAXIOUT) -self:_UpdateMenu() -end -end -end -function FLIGHTGROUP:onafterTakeoff(From,Event,To,airbase) -self:T(self.lid..string.format("Flight takeoff from %s",airbase and airbase:GetName()or"unknown airbase")) -if self.flightcontrol and airbase and self.flightcontrol.airbasename==airbase:GetName()then -self.flightcontrol:_RemoveFlight(self) -self.flightcontrol=nil -end -end -function FLIGHTGROUP:onafterAirborne(From,Event,To) -self:T(self.lid..string.format("Flight airborne")) -if self.isAI then -self:_CheckGroupDone(1) -else -self:_UpdateMenu() -end -end -function FLIGHTGROUP:onafterLanding(From,Event,To) -self:T(self.lid..string.format("Flight is landing")) -self:_SetElementStatusAll(OPSGROUP.ElementStatus.LANDING) -end -function FLIGHTGROUP:onafterLanded(From,Event,To,airbase) -self:T(self.lid..string.format("Flight landed at %s",airbase and airbase:GetName()or"unknown place")) -if self.flightcontrol and airbase and self.flightcontrol.airbasename==airbase:GetName()then -self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.TAXIINB) -end -end -function FLIGHTGROUP:onafterLandedAt(From,Event,To) -self:T(self.lid..string.format("Flight landed at")) -end -function FLIGHTGROUP:onafterArrived(From,Event,To) -self:T(self.lid..string.format("Flight arrived")) -if self.flightcontrol then -self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.ARRIVED) -end -if not self.airwing then -self:Despawn(5*60) -end -end -function FLIGHTGROUP:onafterDead(From,Event,To) -if self.flightcontrol then -self.flightcontrol:_RemoveFlight(self) -self.flightcontrol=nil -end -if self.Ndestroyed==#self.elements then -if self.squadron then -self.squadron:DelGroup(self.groupname) -end -else -if self.airwing then -self.airwing:AddAsset(self.group,1) -end -end -self:GetParent(self).onafterDead(self,From,Event,To) -end -function FLIGHTGROUP:onbeforeUpdateRoute(From,Event,To,n) -local allowed=true -local trepeat=nil -if self:IsAlive()then -self:T3(self.lid.."Update route possible. Group is ALIVE") -elseif self:IsDead()then -self:E(self.lid.."Update route denied. Group is DEAD!") -allowed=false -else -self:T(self.lid.."Update route denied ==> checking back in 5 sec") -trepeat=-5 -allowed=false -end -if n and n<1 then -self:E(self.lid.."Update route denied because waypoint n<1!") -allowed=false -end -if not self.currentwp then -self:E(self.lid.."Update route denied because self.currentwp=nil!") -allowed=false -end -local N=n or self.currentwp+1 -if not N or N<1 then -self:E(self.lid.."Update route denied because N=nil or N<1") -trepeat=-5 -allowed=false -end -if self.taskcurrent>0 then -local task=self:GetTaskByID(self.taskcurrent) -if task then -if task.dcstask.id=="PatrolZone"then -else -local taskname=task and task.description or"No description" -self:E(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s",self.taskcurrent,tostring(taskname))) -allowed=false -end -else -self:T(self.lid..string.format("WARNING: before update route taskcurrent=%d>0 but no task?!",self.taskcurrent)) -allowed=false -end -end -if not self.isAI then -allowed=false -end -self:T2(self.lid..string.format("Onbefore Updateroute allowed=%s state=%s repeat in %s",tostring(allowed),self:GetState(),tostring(trepeat))) -if trepeat then -self:__UpdateRoute(trepeat,n) -end -return allowed -end -function FLIGHTGROUP:onafterUpdateRoute(From,Event,To,n) -n=n or self.currentwp+1 -self:_UpdateWaypointTasks(n) -local wp={} -local speed=self.group and self.group:GetVelocityKMH()or 100 -local current=self.group:GetCoordinate():WaypointAir(COORDINATE.WaypointAltType.BARO,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,speed,true,nil,{},"Current") -table.insert(wp,current) -local Nwp=self.waypoints and#self.waypoints or 0 -for i=n,Nwp do -table.insert(wp,self.waypoints[i]) -end -local hb=self.homebase and self.homebase:GetName()or"unknown" -local db=self.destbase and self.destbase:GetName()or"unknown" -self:T(self.lid..string.format("Updating route for WP #%d-%d homebase=%s destination=%s",n,#wp,hb,db)) -if#wp>1 then -self:Route(wp) -else -if self:IsAirborne()then -self:T(self.lid.."No waypoints left ==> CheckGroupDone") -self:_CheckGroupDone() -end -end -end -function FLIGHTGROUP:onafterRespawn(From,Event,To,Template) -self:T(self.lid.."Respawning group!") -local template=UTILS.DeepCopy(Template or self.template) -if self.group and self.group:InAir()then -template.lateActivation=false -self.respawning=true -self.group=self.group:Respawn(template) -end -end -function FLIGHTGROUP:onafterOutOfMissilesAA(From,Event,To) -self:I(self.lid.."Group is out of AA Missiles!") -if self.outofAAMrtb then -local airbase=self.destbase or self.homebase -self:__RTB(-5,airbase) -end -end -function FLIGHTGROUP:onafterOutOfMissilesAG(From,Event,To) -self:I(self.lid.."Group is out of AG Missiles!") -if self.outofAGMrtb then -local airbase=self.destbase or self.homebase -self:__RTB(-5,airbase) -end -end -function FLIGHTGROUP:_CheckGroupDone(delay) -if self:IsAlive()and self.isAI then -if delay and delay>0 then -self:ScheduleOnce(delay,FLIGHTGROUP._CheckGroupDone,self) -else -if self.missionpaused then -self:UnpauseMission() -return -end -if self:IsEngaging()then -return -end -local nTasks=self:CountRemainingTasks() -local nMissions=self:CountRemainingMissison() -if self.passedfinalwp then -if self.currentmission==nil and self.taskcurrent==0 then -if nTasks==0 and nMissions==0 then -local destbase=self.destbase or self.homebase -local destzone=self.destzone or self.homezone -if destbase then -self:T(self.lid.."Passed Final WP and No current and/or future missions/task ==> RTB!") -self:__RTB(-3,destbase) -elseif destzone then -self:T(self.lid.."Passed Final WP and No current and/or future missions/task ==> RTZ!") -self:__RTZ(-3,destzone) -else -self:T(self.lid.."Passed Final WP and NO Tasks/Missions left. No DestBase or DestZone ==> Wait!") -self:__Wait(-1) -end -else -self:T(self.lid..string.format("Passed Final WP but Tasks=%d or Missions=%d left in the queue. Wait!",nTasks,nMissions)) -self:__Wait(-1) -end -else -self:T(self.lid..string.format("Passed Final WP but still have current Task (#%s) or Mission (#%s) left to do",tostring(self.taskcurrent),tostring(self.currentmission))) -end -else -self:T(self.lid..string.format("Flight (status=%s) did NOT pass the final waypoint yet ==> update route",self:GetState())) -self:__UpdateRoute(-1) -end -end -end -end -function FLIGHTGROUP:onbeforeRTB(From,Event,To,airbase,SpeedTo,SpeedHold) -if self:IsAlive()then -local allowed=true -local Tsuspend=nil -if airbase==nil then -self:E(self.lid.."ERROR: Airbase is nil in RTB() call!") -allowed=false -end -if airbase and airbase:GetCoalition()~=self.group:GetCoalition()and airbase:GetCoalition()>0 then -self:E(self.lid..string.format("ERROR: Wrong airbase coalition %d in RTB() call! We allow only same as group %d or neutral airbases 0.",airbase:GetCoalition(),self.group:GetCoalition())) -allowed=false -end -if not self.group:IsAirborne(true)then -self:I(self.lid..string.format("WARNING: Group is not AIRBORNE ==> RTB event is suspended for 20 sec.")) -allowed=false -Tsuspend=-20 -local groupspeed=self.group:GetVelocityMPS() -if groupspeed<=1 then self.RTBRecallCount=self.RTBRecallCount+1 end -if self.RTBRecallCount>6 then -self:Despawn(5) -end -end -if not(self:IsFuelLow()or self:IsFuelCritical())then -local Ntot,Nsched,Nwp=self:CountRemainingTasks() -if self.taskcurrent>0 then -self:I(self.lid..string.format("WARNING: Got current task ==> RTB event is suspended for 10 sec.")) -Tsuspend=-10 -allowed=false -end -if Nsched>0 then -self:I(self.lid..string.format("WARNING: Still got %d SCHEDULED tasks in the queue ==> RTB event is suspended for 10 sec.",Nsched)) -Tsuspend=-10 -allowed=false -end -if Nwp>0 then -self:I(self.lid..string.format("WARNING: Still got %d WAYPOINT tasks in the queue ==> RTB event is suspended for 10 sec.",Nwp)) -Tsuspend=-10 -allowed=false -end -end -if Tsuspend and not allowed then -self:__RTB(Tsuspend,airbase,SpeedTo,SpeedHold) -end -return allowed -else -self:E(self.lid.."WARNING: Group is not alive! RTB call not allowed.") -return false -end -end -function FLIGHTGROUP:onafterRTB(From,Event,To,airbase,SpeedTo,SpeedHold,SpeedLand) -self:T(self.lid..string.format("RTB: event=%s: %s --> %s to %s",Event,From,To,airbase:GetName())) -self.destbase=airbase -self.Tholding=nil -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -local mystatus=mission:GetGroupStatus(self) -if not(mystatus==AUFTRAG.GroupStatus.DONE or mystatus==AUFTRAG.GroupStatus.CANCELLED)then -local text=string.format("Canceling mission %s in state=%s",mission.name,mission.status) -self:T(self.lid..text) -self:MissionCancel(mission) -end -end -SpeedTo=SpeedTo or UTILS.KmphToKnots(self.speedCruise) -SpeedHold=SpeedHold or(self.ishelo and 80 or 250) -SpeedLand=SpeedLand or(self.ishelo and 40 or 170) -local text=string.format("Flight group set to hold at airbase %s. SpeedTo=%d, SpeedHold=%d, SpeedLand=%d",airbase:GetName(),SpeedTo,SpeedHold,SpeedLand) -self:T(self.lid..text) -local althold=self.ishelo and 1000+math.random(10)*100 or math.random(4,10)*1000 -local c0=self.group:GetCoordinate() -local p0=airbase:GetZone():GetRandomCoordinate():SetAltitude(UTILS.FeetToMeters(althold)) -local p1=nil -local wpap=nil -local fc=_DATABASE:GetFlightControl(airbase:GetName()) -if fc then -local HoldingPoint=fc:_GetHoldingpoint(self) -p0=HoldingPoint.pos0 -p1=HoldingPoint.pos1 -if self.Debug then -p0:MarkToAll("Holding point P0") -p1:MarkToAll("Holding point P1") -end -self:SetFlightControl(fc) -self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.INBOUND) -end -local x1=self.ishelo and UTILS.NMToMeters(5.0)or UTILS.NMToMeters(10) -local x2=self.ishelo and UTILS.NMToMeters(2.5)or UTILS.NMToMeters(5) -local alpha=math.rad(3) -local h1=x1*math.tan(alpha) -local h2=x2*math.tan(alpha) -local runway=airbase:GetActiveRunway() -self.flaghold:Set(0) -local holdtime=5*60 -if fc or self.airboss then -holdtime=nil -end -local TaskArrived=self.group:TaskFunction("FLIGHTGROUP._ReachedHolding",self) -local TaskOrbit=self.group:TaskOrbit(p0,nil,UTILS.KnotsToMps(SpeedHold),p1) -local TaskLand=self.group:TaskCondition(nil,self.flaghold.UserFlagName,1,nil,holdtime) -local TaskHold=self.group:TaskControlled(TaskOrbit,TaskLand) -local TaskKlar=self.group:TaskFunction("FLIGHTGROUP._ClearedToLand",self) -local wp={} -wp[#wp+1]=c0:WaypointAir(nil,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,UTILS.KnotsToKmph(SpeedTo),true,nil,{},"Current Pos") -wp[#wp+1]=p0:WaypointAir(nil,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,UTILS.KnotsToKmph(SpeedTo),true,nil,{TaskArrived,TaskHold,TaskKlar},"Holding Point") -if airbase:GetAirbaseCategory()==Airbase.Category.AIRDROME then -local papp=airbase:GetCoordinate():Translate(x1,runway.heading-180):SetAltitude(h1) -wp[#wp+1]=papp:WaypointAirTurningPoint(nil,UTILS.KnotsToKmph(SpeedLand),{},"Final Approach") -local pland=airbase:GetCoordinate():Translate(x2,runway.heading-180):SetAltitude(h2) -wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand),airbase,{},"Landing") -elseif airbase:GetAirbaseCategory()==Airbase.Category.SHIP then -local pland=airbase:GetCoordinate() -wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand),airbase,{},"Landing") -end -if self.isAI then -local routeto=false -if fc or world.event.S_EVENT_KILL then -routeto=true -end -if routeto then -self:Route(wp,1) -else -local Template=self.group:GetTemplate() -Template.route.points=wp -self:Respawn(Template) -end -end -end -function FLIGHTGROUP:onbeforeWait(From,Event,To,Coord,Altitude,Speed) -local allowed=true -local Tsuspend=nil -local Ntot,Nsched,Nwp=self:CountRemainingTasks() -if self.taskcurrent>0 then -self:I(self.lid..string.format("WARNING: Got current task ==> WAIT event is suspended for 10 sec.")) -Tsuspend=-10 -allowed=false -end -if Nsched>0 then -self:I(self.lid..string.format("WARNING: Still got %d SCHEDULED tasks in the queue ==> WAIT event is suspended for 10 sec.",Nsched)) -Tsuspend=-10 -allowed=false -end -if Nwp>0 then -self:I(self.lid..string.format("WARNING: Still got %d WAYPOINT tasks in the queue ==> WAIT event is suspended for 10 sec.",Nwp)) -Tsuspend=-10 -allowed=false -end -if Tsuspend and not allowed then -self:__Wait(Tsuspend,Coord,Altitude,Speed) -end -return allowed -end -function FLIGHTGROUP:onafterWait(From,Event,To,Coord,Altitude,Speed) -Coord=Coord or self.group:GetCoordinate() -Altitude=Altitude or(self.ishelo and 1000 or 10000) -Speed=Speed or(self.ishelo and 80 or 250) -local text=string.format("Flight group set to wait/orbit at altitude %d m and speed %.1f km/h",Altitude,Speed) -self:T(self.lid..text) -local TaskOrbit=self.group:TaskOrbit(Coord,UTILS.FeetToMeters(Altitude),UTILS.KnotsToMps(Speed)) -self:SetTask(TaskOrbit) -end -function FLIGHTGROUP:onafterRefuel(From,Event,To,Coordinate) -local text=string.format("Flight group set to refuel at the nearest tanker") -self:I(self.lid..text) -self:PauseMission() -local TaskRefuel=self.group:TaskRefueling() -local TaskFunction=self.group:TaskFunction("FLIGHTGROUP._FinishedRefuelling",self) -local DCSTasks={TaskRefuel,TaskFunction} -local Speed=self.speedCruise -local coordinate=self.group:GetCoordinate() -Coordinate=Coordinate or coordinate:Translate(UTILS.NMToMeters(5),self.group:GetHeading(),true) -local wp0=coordinate:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true) -local wp9=Coordinate:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true,nil,DCSTasks,"Refuel") -self:Route({wp0,wp9},1) -end -function FLIGHTGROUP:onafterRefueled(From,Event,To) -local text=string.format("Flight group finished refuelling") -self:I(self.lid..text) -self:_CheckGroupDone(1) -end -function FLIGHTGROUP:onafterHolding(From,Event,To) -self.flaghold:Set(0) -self.Tholding=timer.getAbsTime() -local text=string.format("Flight group %s is HOLDING now",self.groupname) -self:T(self.lid..text) -if self.flightcontrol then -self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.HOLDING) -if not self.isAI then -self:_UpdateMenu() -end -elseif self.airboss then -if self.ishelo then -local carrierpos=self.airboss:GetCoordinate() -local carrierheading=self.airboss:GetHeading() -local Distance=UTILS.NMToMeters(5) -local Angle=carrierheading+90 -local altitude=math.random(12,25)*100 -local oc=carrierpos:Translate(Distance,Angle):SetAltitude(altitude,true) -local TaskOrbit=self.group:TaskOrbit(oc,nil,UTILS.KnotsToMps(50)) -local TaskLand=self.group:TaskCondition(nil,self.flaghold.UserFlagName,1) -local TaskHold=self.group:TaskControlled(TaskOrbit,TaskLand) -local TaskKlar=self.group:TaskFunction("FLIGHTGROUP._ClearedToLand",self) -local DCSTask=self.group:TaskCombo({TaskOrbit,TaskHold,TaskKlar}) -self:SetTask(DCSTask) -end -end -end -function FLIGHTGROUP:onafterEngageTarget(From,Event,To,Target) -local DCStask=nil -if Target:IsInstanceOf("UNIT")or Target:IsInstanceOf("STATIC")then -DCStask=self:GetGroup():TaskAttackUnit(Target,true) -elseif Target:IsInstanceOf("GROUP")then -DCStask=self:GetGroup():TaskAttackGroup(Target,nil,nil,nil,nil,nil,nil,true) -elseif Target:IsInstanceOf("SET_UNIT")then -local DCSTasks={} -for _,_unit in pairs(Target:GetSet())do -local unit=_unit -local task=self:GetGroup():TaskAttackUnit(unit,true) -table.insert(DCSTasks) -end -DCStask=self:GetGroup():TaskCombo(DCSTasks) -elseif Target:IsInstanceOf("SET_GROUP")then -local DCSTasks={} -for _,_unit in pairs(Target:GetSet())do -local unit=_unit -local task=self:GetGroup():TaskAttackGroup(Target,nil,nil,nil,nil,nil,nil,true) -table.insert(DCSTasks) -end -DCStask=self:GetGroup():TaskCombo(DCSTasks) -else -self:E("ERROR: unknown Target in EngageTarget! Needs to be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP") -return -end -local Task=self:NewTaskScheduled(DCStask,1,"Engage_Target",0) -Task.backupROE=self:GetROE() -self:SwitchROE(ENUMS.ROE.OpenFire) -local mission=self:GetMissionCurrent() -if mission then -self:PauseMission() -end -self:TaskExecute(Task) -end -function FLIGHTGROUP:onafterDisengage(From,Event,To) -self:T(self.lid.."Disengage target") -end -function FLIGHTGROUP:onbeforeLandAt(From,Event,To,Coordinate,Duration) -return self.ishelo -end -function FLIGHTGROUP:onafterLandAt(From,Event,To,Coordinate,Duration) -Duration=Duration or 600 -Coordinate=Coordinate or self:GetCoordinate() -local DCStask=self.group:TaskLandAtVec2(Coordinate:GetVec2(),Duration) -local Task=self:NewTaskScheduled(DCStask,1,"Task_Land_At",0) -self:TaskExecute(Task) -end -function FLIGHTGROUP:onafterFuelLow(From,Event,To) -local text=string.format("Low fuel for flight group %s",self.groupname) -self:I(self.lid..text) -self.fuellow=true -local airbase=self.destbase or self.homebase -if self.airwing then -local tanker=self.airwing:GetTankerForFlight(self) -if tanker then -self:I(self.lid..string.format("Send to refuel at tanker %s",tanker.flightgroup:GetName())) -local coordinate=self:GetCoordinate():GetIntermediateCoordinate(tanker.flightgroup:GetCoordinate(),0.75) -self:Refuel(coordinate) -else -if airbase and self.fuellowrtb then -self:RTB(airbase) -end -end -else -if self.fuellowrefuel and self.refueltype then -local tanker=self:FindNearestTanker(50) -if tanker then -self:I(self.lid..string.format("Send to refuel at tanker %s",tanker:GetName())) -local coordinate=self:GetCoordinate():GetIntermediateCoordinate(tanker:GetCoordinate(),0.75) -self:Refuel(coordinate) -return -end -end -if airbase and self.fuellowrtb then -self:RTB(airbase) -end -end -end -function FLIGHTGROUP:onafterFuelCritical(From,Event,To) -local text=string.format("Critical fuel for flight group %s",self.groupname) -self:I(self.lid..text) -self.fuelcritical=true -local airbase=self.destbase or self.homebase -if airbase and self.fuelcriticalrtb and not self:IsGoing4Fuel()then -self:RTB(airbase) -end -end -function FLIGHTGROUP:onafterStop(From,Event,To) -if self:IsAlive()then -if self.flightcontrol then -for _,_element in pairs(self.elements)do -local element=_element -self:_SetElementParkingFree(element) -end -end -end -self:UnHandleEvent(EVENTS.Birth) -self:UnHandleEvent(EVENTS.EngineStartup) -self:UnHandleEvent(EVENTS.Takeoff) -self:UnHandleEvent(EVENTS.Land) -self:UnHandleEvent(EVENTS.EngineShutdown) -self:UnHandleEvent(EVENTS.PilotDead) -self:UnHandleEvent(EVENTS.Ejection) -self:UnHandleEvent(EVENTS.Crash) -self:UnHandleEvent(EVENTS.RemoveUnit) -self:GetParent(self).onafterStop(self,From,Event,To) -_DATABASE.FLIGHTGROUPS[self.groupname]=nil -end -function FLIGHTGROUP._ReachedHolding(group,flightgroup) -flightgroup:T2(flightgroup.lid..string.format("Group reached holding point")) -flightgroup:__Holding(-1) -end -function FLIGHTGROUP._ClearedToLand(group,flightgroup) -flightgroup:T2(flightgroup.lid..string.format("Group was cleared to land")) -flightgroup:__Landing(-1) -end -function FLIGHTGROUP._FinishedRefuelling(group,flightgroup) -flightgroup:T2(flightgroup.lid..string.format("Group finished refueling")) -flightgroup:__Refueled(-1) -end -function FLIGHTGROUP:_InitGroup() -if self.groupinitialized then -self:E(self.lid.."WARNING: Group was already initialized!") -return -end -local group=self.group -self.template=group:GetTemplate() -self.isAircraft=true -self.isNaval=false -self.isGround=false -self.ishelo=group:IsHelicopter() -self.isUncontrolled=self.template.uncontrolled -self.isLateActivated=self.template.lateActivation -self.speedMax=group:GetSpeedMax() -local speedCruiseLimit=self.ishelo and UTILS.KnotsToKmph(80)or UTILS.KnotsToKmph(350) -self.speedCruise=math.min(self.speedMax*0.7,speedCruiseLimit) -self.ammo=self:GetAmmoTot() -self.radio.Freq=tonumber(self.template.frequency) -self.radio.Modu=tonumber(self.template.modulation) -self.radio.On=self.template.communication -local callsign=self.template.units[1].callsign -if type(callsign)=="number"then -local cs=tostring(callsign) -callsign={} -callsign[1]=cs:sub(1,1) -callsign[2]=cs:sub(2,2) -callsign[3]=cs:sub(3,3) -end -self.callsign.NumberSquad=callsign[1] -self.callsign.NumberGroup=callsign[2] -self.callsign.NumberElement=callsign[3] -self.callsign.NameSquad=UTILS.GetCallsignName(self.callsign.NumberSquad) -if self.ishelo then -self.optionDefault.Formation=ENUMS.Formation.RotaryWing.EchelonLeft.D300 -else -self.optionDefault.Formation=ENUMS.Formation.FixedWing.EchelonLeft.Group -end -self:SetDefaultTACAN(nil,nil,nil,nil,true) -self.tacan=UTILS.DeepCopy(self.tacanDefault) -self.isAI=not self:_IsHuman(group) -if not self.isAI then -self.menu=self.menu or{} -self.menu.atc=self.menu.atc or{} -self.menu.atc.root=self.menu.atc.root or MENU_GROUP:New(self.group,"ATC") -end -for _,unit in pairs(self.group:GetUnits())do -local element=self:AddElementByName(unit:GetName()) -end -local unit=self.group:GetUnit(1) -if unit then -self.rangemax=unit:GetRange() -self.descriptors=unit:GetDesc() -self.actype=unit:GetTypeName() -self.ceiling=self.descriptors.Hmax -self.tankertype=select(2,unit:IsTanker()) -self.refueltype=select(2,unit:IsRefuelable()) -if self.verbose>=1 then -local text=string.format("Initialized Flight Group %s:\n",self.groupname) -text=text..string.format("Unit type = %s\n",self.actype) -text=text..string.format("Speed max = %.1f Knots\n",UTILS.KmphToKnots(self.speedMax)) -text=text..string.format("Range max = %.1f km\n",self.rangemax/1000) -text=text..string.format("Ceiling = %.1f feet\n",UTILS.MetersToFeet(self.ceiling)) -text=text..string.format("Tanker type = %s\n",tostring(self.tankertype)) -text=text..string.format("Refuel type = %s\n",tostring(self.refueltype)) -text=text..string.format("AI = %s\n",tostring(self.isAI)) -text=text..string.format("Helicopter = %s\n",tostring(self.group:IsHelicopter())) -text=text..string.format("Elements = %d\n",#self.elements) -text=text..string.format("Waypoints = %d\n",#self.waypoints) -text=text..string.format("Radio = %.1f MHz %s %s\n",self.radio.Freq,UTILS.GetModulationName(self.radio.Modu),tostring(self.radio.On)) -text=text..string.format("Ammo = %d (G=%d/R=%d/B=%d/M=%d)\n",self.ammo.Total,self.ammo.Guns,self.ammo.Rockets,self.ammo.Bombs,self.ammo.Missiles) -text=text..string.format("FSM state = %s\n",self:GetState()) -text=text..string.format("Is alive = %s\n",tostring(self.group:IsAlive())) -text=text..string.format("LateActivate = %s\n",tostring(self:IsLateActivated())) -text=text..string.format("Uncontrolled = %s\n",tostring(self:IsUncontrolled())) -text=text..string.format("Start Air = %s\n",tostring(self:IsTakeoffAir())) -text=text..string.format("Start Cold = %s\n",tostring(self:IsTakeoffCold())) -text=text..string.format("Start Hot = %s\n",tostring(self:IsTakeoffHot())) -text=text..string.format("Start Rwy = %s\n",tostring(self:IsTakeoffRunway())) -self:I(self.lid..text) -end -self.groupinitialized=true -end -return self -end -function FLIGHTGROUP:AddElementByName(unitname) -local unit=UNIT:FindByName(unitname) -if unit then -local element={} -element.name=unitname -element.unit=unit -element.status=OPSGROUP.ElementStatus.INUTERO -element.group=unit:GetGroup() -local unittemplate=element.unit:GetTemplate() -element.modex=unittemplate.onboard_num -element.skill=unittemplate.skill -element.payload=unittemplate.payload -element.pylons=unittemplate.payload and unittemplate.payload.pylons or nil -element.fuelmass0=unittemplate.payload and unittemplate.payload.fuel or 0 -element.fuelmass=element.fuelmass0 -element.fuelrel=element.unit:GetFuel() -element.category=element.unit:GetUnitCategory() -element.categoryname=element.unit:GetCategoryName() -element.callsign=element.unit:GetCallsign() -element.size=element.unit:GetObjectSize() -if element.skill=="Client"or element.skill=="Player"then -element.ai=false -element.client=CLIENT:FindByName(unitname) -else -element.ai=true -end -local text=string.format("Adding element %s: status=%s, skill=%s, modex=%s, fuelmass=%.1f (%d), category=%d, categoryname=%s, callsign=%s, ai=%s", -element.name,element.status,element.skill,element.modex,element.fuelmass,element.fuelrel*100,element.category,element.categoryname,element.callsign,tostring(element.ai)) -self:T(self.lid..text) -table.insert(self.elements,element) -if unit:IsAlive()then -self:ElementSpawned(element) -end -return element -end -return nil -end -function FLIGHTGROUP:GetHomebaseFromWaypoints() -local wp=self:GetWaypoint(1) -if wp then -if wp and wp.action and wp.action==COORDINATE.WaypointAction.FromParkingArea -or wp.action==COORDINATE.WaypointAction.FromParkingAreaHot -or wp.action==COORDINATE.WaypointAction.FromRunway then -local airbaseID=nil -if wp.airdromeId then -airbaseID=wp.airdromeId -else -airbaseID=-wp.helipadId -end -local airbase=AIRBASE:FindByID(airbaseID) -return airbase -end -end -return nil -end -function FLIGHTGROUP:FindNearestAirbase(Radius) -local coord=self:GetCoordinate() -local dmin=math.huge -local airbase=nil -for _,_airbase in pairs(AIRBASE.GetAllAirbases())do -local ab=_airbase -local coalitionAB=ab:GetCoalition() -if coalitionAB==self:GetCoalition()or coalitionAB==coalition.side.NEUTRAL then -if airbase then -local d=ab:GetCoordinate():Get2DDistance(coord) -if d %s Destination",#self.waypoints,self.homebase and self.homebase:GetName()or"unknown",self.destbase and self.destbase:GetName()or"uknown")) -if#self.waypoints>0 then -if#self.waypoints==1 then -self.passedfinalwp=true -end -end -return self -end -function FLIGHTGROUP:AddWaypoint(Coordinate,Speed,AfterWaypointWithID,Altitude,Updateroute) -local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) -if wpnumber>self.currentwp then -self.passedfinalwp=false -end -Speed=Speed or 350 -local wp=Coordinate:WaypointAir(COORDINATE.WaypointAltType.BARO,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,UTILS.KnotsToKmph(Speed),true,nil,{}) -local waypoint=self:_CreateWaypoint(wp) -if Altitude then -waypoint.alt=UTILS.FeetToMeters(Altitude) -end -self:_AddWaypoint(waypoint,wpnumber) -self:T(self.lid..string.format("Adding AIR waypoint #%d, speed=%.1f knots. Last waypoint passed was #%s. Total waypoints #%d",wpnumber,Speed,self.currentwp,#self.waypoints)) -if Updateroute==nil or Updateroute==true then -self:__UpdateRoute(-1) -end -return waypoint -end -function FLIGHTGROUP:_IsElement(unitname) -for _,_element in pairs(self.elements)do -local element=_element -if element.name==unitname then -return true -end -end -return false -end -function FLIGHTGROUP:_SetElementParkingAt(Element,Spot) -Element.parking=Spot -if Spot then -self:T(self.lid..string.format("Element %s is parking on spot %d",Element.name,Spot.TerminalID)) -if self.flightcontrol then -self.flightcontrol:SetParkingOccupied(Element.parking,Element.name) -end -end -end -function FLIGHTGROUP:_SetElementParkingFree(Element) -if Element.parking then -if self.flightcontrol then -self.flightcontrol:SetParkingFree(Element.parking) -end -Element.parking=nil -end -end -function FLIGHTGROUP:_GetOnboardNumber(unitname) -local group=UNIT:FindByName(unitname):GetGroup() -local units=group:GetTemplate().units -local numbers={} -for _,unit in pairs(units)do -if unitname==unit.name then -return tostring(unit.onboard_num) -end -end -return nil -end -function FLIGHTGROUP:_IsHumanUnit(unit) -local playerunit=self:_GetPlayerUnitAndName(unit:GetName()) -if playerunit then -return true -else -return false -end -end -function FLIGHTGROUP:_IsHuman(group) -local units=group:GetUnits() -for _,_unit in pairs(units)do -local human=self:_IsHumanUnit(_unit) -if human then -return true -end -end -return false -end -function FLIGHTGROUP:_GetPlayerUnitAndName(_unitName) -self:F2(_unitName) -if _unitName~=nil then -local DCSunit=Unit.getByName(_unitName) -if DCSunit then -local playername=DCSunit:getPlayerName() -local unit=UNIT:Find(DCSunit) -if DCSunit and unit and playername then -return unit,playername -end -end -end -return nil,nil -end -function FLIGHTGROUP:GetParkingSpot(element,maxdist,airbase) -local coord=element.unit:GetCoordinate() -airbase=airbase or self:GetClosestAirbase() -local parking=airbase:GetParkingSpotsTable() -local spot=nil -local dist=nil -local distmin=math.huge -for _,_parking in pairs(parking)do -local parking=_parking -dist=coord:Get2DDistance(parking.Coordinate) -if distsafedist) -return safe -end -local function _clients() -local clients=_DATABASE.CLIENTS -local coords={} -for clientname,client in pairs(clients)do -local template=_DATABASE:GetGroupTemplateFromUnitName(clientname) -local units=template.units -for i,unit in pairs(units)do -local coord=COORDINATE:New(unit.x,unit.alt,unit.y) -coords[unit.name]=coord -end -end -return coords -end -local airbasecategory=airbase:GetAirbaseCategory() -local parkingdata=airbase:GetParkingSpotsTable() -local obstacles={} -for _,_parkingspot in pairs(parkingdata)do -local parkingspot=_parkingspot -local _,_,_,_units,_statics,_sceneries=parkingspot.Coordinate:ScanObjects(scanradius,scanunits,scanstatics,scanscenery) -for _,_unit in pairs(_units)do -local unit=_unit -local _coord=unit:GetCoordinate() -local _size=self:_GetObjectSize(unit:GetDCSObject()) -local _name=unit:GetName() -table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="unit"}) -end -local clientcoords=_clients() -for clientname,_coord in pairs(clientcoords)do -table.insert(obstacles,{coord=_coord,size=15,name=clientname,type="client"}) -end -for _,static in pairs(_statics)do -local _vec3=static:getPoint() -local _coord=COORDINATE:NewFromVec3(_vec3) -local _name=static:getName() -local _size=self:_GetObjectSize(static) -table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="static"}) -end -for _,scenery in pairs(_sceneries)do -local _vec3=scenery:getPoint() -local _coord=COORDINATE:NewFromVec3(_vec3) -local _name=scenery:getTypeName() -local _size=self:_GetObjectSize(scenery) -table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="scenery"}) -end -end -local parking={} -local terminaltype=self:_GetTerminal(self.attribute,airbase:GetAirbaseCategory()) -for i,_element in pairs(self.elements)do -local element=_element -local gotit=false -for _,_parkingspot in pairs(parkingdata)do -local parkingspot=_parkingspot -if AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype)then -local free=true -local problem=nil -if verysafe and parkingspot.TOAC then -free=false -self:T2(self.lid..string.format("Parking spot %d is occupied by other aircraft taking off (TOAC).",parkingspot.TerminalID)) -end -for _,obstacle in pairs(obstacles)do -local dist=parkingspot.Coordinate:Get2DDistance(obstacle.coord) -local safe=_overlap(element.size,obstacle.size,dist) -if not safe then -free=false -problem=obstacle -problem.dist=dist -break -end -end -if self.flightcontrol and self.flightcontrol.airbasename==airbase:GetName()then -local problem=self.flightcontrol:IsParkingReserved(parkingspot)or self.flightcontrol:IsParkingOccupied(parkingspot) -if problem then -free=false -end -end -if free then -table.insert(parking,parkingspot) -self:T2(self.lid..string.format("Parking spot %d is free for element %s!",parkingspot.TerminalID,element.name)) -table.insert(obstacles,{coord=parkingspot.Coordinate,size=element.size,name=element.name,type="element"}) -gotit=true -break -else -self:T2(self.lid..string.format("Parking spot %d is occupied or not big enough!",parkingspot.TerminalID)) -end -end -end -if not gotit then -self:E(self.lid..string.format("WARNING: No free parking spot for element %s",element.name)) -return nil -end -end -return parking -end -function FLIGHTGROUP:_GetObjectSize(DCSobject) -local DCSdesc=DCSobject:getDesc() -if DCSdesc.box then -local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) -local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) -local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) -return math.max(x,z),x,y,z -end -return 0,0,0,0 -end -function FLIGHTGROUP:_GetAttribute() -local attribute=FLIGHTGROUP.Attribute.OTHER -local group=self.group -if group then -local transportplane=group:HasAttribute("Transports")and group:HasAttribute("Planes") -local awacs=group:HasAttribute("AWACS") -local fighter=group:HasAttribute("Fighters")or group:HasAttribute("Interceptors")or group:HasAttribute("Multirole fighters")or(group:HasAttribute("Bombers")and not group:HasAttribute("Strategic bombers")) -local bomber=group:HasAttribute("Strategic bombers") -local tanker=group:HasAttribute("Tankers") -local uav=group:HasAttribute("UAVs") -local transporthelo=group:HasAttribute("Transport helicopters") -local attackhelicopter=group:HasAttribute("Attack helicopters") -if transportplane then -attribute=FLIGHTGROUP.Attribute.AIR_TRANSPORTPLANE -elseif awacs then -attribute=FLIGHTGROUP.Attribute.AIR_AWACS -elseif fighter then -attribute=FLIGHTGROUP.Attribute.AIR_FIGHTER -elseif bomber then -attribute=FLIGHTGROUP.Attribute.AIR_BOMBER -elseif tanker then -attribute=FLIGHTGROUP.Attribute.AIR_TANKER -elseif transporthelo then -attribute=FLIGHTGROUP.Attribute.AIR_TRANSPORTHELO -elseif attackhelicopter then -attribute=FLIGHTGROUP.Attribute.AIR_ATTACKHELO -elseif uav then -attribute=FLIGHTGROUP.Attribute.AIR_UAV -end -end -return attribute -end -function FLIGHTGROUP:_GetTerminal(_attribute,_category) -local _terminal=AIRBASE.TerminalType.OpenBig -if _attribute==FLIGHTGROUP.Attribute.AIR_FIGHTER then -_terminal=AIRBASE.TerminalType.FighterAircraft -elseif _attribute==FLIGHTGROUP.Attribute.AIR_BOMBER or _attribute==FLIGHTGROUP.Attribute.AIR_TRANSPORTPLANE or _attribute==FLIGHTGROUP.Attribute.AIR_TANKER or _attribute==FLIGHTGROUP.Attribute.AIR_AWACS then -_terminal=AIRBASE.TerminalType.OpenBig -elseif _attribute==FLIGHTGROUP.Attribute.AIR_TRANSPORTHELO or _attribute==FLIGHTGROUP.Attribute.AIR_ATTACKHELO then -_terminal=AIRBASE.TerminalType.HelicopterUsable -else -end -if _category==Airbase.Category.SHIP then -if not(_attribute==FLIGHTGROUP.Attribute.AIR_TRANSPORTHELO or _attribute==FLIGHTGROUP.Attribute.AIR_ATTACKHELO)then -_terminal=AIRBASE.TerminalType.OpenMedOrBig -end -end -return _terminal -end -function FLIGHTGROUP:_UpdateMenu(delay) -if delay and delay>0 then -self:I(self.lid..string.format("FF updating menu in %.1f sec",delay)) -self:ScheduleOnce(delay,FLIGHTGROUP._UpdateMenu,self) -else -self:I(self.lid.."FF updating menu NOW") -local position=self.group:GetCoordinate() -local fc={} -for airbasename,_flightcontrol in pairs(_DATABASE.FLIGHTCONTROLS)do -local airbase=AIRBASE:FindByName(airbasename) -local coord=airbase:GetCoordinate() -local dist=coord:Get2DDistance(position) -local fcitem={airbasename=airbasename,dist=dist} -table.insert(fc,fcitem) -end -local function _sort(a,b) -return a.distTstop then -self:E(string.format("ERROR:Into wind stop time %s lies before start time %s. Input rejected!",UTILS.SecondsToClock(Tstart),UTILS.SecondsToClock(Tstop))) -return self -end -if Tstop<=Tnow then -self:E(string.format("WARNING: Into wind stop time %s already over. Tnow=%s! Input rejected.",UTILS.SecondsToClock(Tstop),UTILS.SecondsToClock(Tnow))) -return self -end -self.intowindcounter=self.intowindcounter+1 -local recovery={} -recovery.Tstart=Tstart -recovery.Tstop=Tstop -recovery.Open=false -recovery.Over=false -recovery.Speed=speed or 20 -recovery.Uturn=uturn and uturn or false -recovery.Offset=offset or 0 -recovery.Id=self.intowindcounter -return recovery -end -function NAVYGROUP:AddTurnIntoWind(starttime,stoptime,speed,uturn,offset) -local recovery=self:_CreateTurnIntoWind(starttime,stoptime,speed,uturn,offset) -table.insert(self.Qintowind,recovery) -return recovery -end -function NAVYGROUP:RemoveTurnIntoWind(IntoWindData) -if self.intowind and self.intowind.Id==IntoWindData.Id then -self:TurnIntoWindStop() -return -end -for i,_tiw in pairs(self.Qintowind)do -local tiw=_tiw -if tiw.Id==IntoWindData.Id then -table.remove(self.Qintowind,i) -break -end -end -return self -end -function NAVYGROUP:IsHolding() -return self:Is("Holding") -end -function NAVYGROUP:IsCruising() -return self:Is("Cruising") -end -function NAVYGROUP:IsOnDetour() -return self:Is("OnDetour") -end -function NAVYGROUP:IsDiving() -return self:Is("Diving") -end -function NAVYGROUP:IsTurning() -return self.turning -end -function NAVYGROUP:IsSteamingIntoWind() -if self.intowind then -return true -else -return false -end -end -function NAVYGROUP:onbeforeStatus(From,Event,To) -if self:IsDead()then -self:T(self.lid..string.format("Onbefore Status DEAD ==> false")) -return false -elseif self:IsStopped()then -self:T(self.lid..string.format("Onbefore Status STOPPED ==> false")) -return false -end -return true -end -function NAVYGROUP:onafterStatus(From,Event,To) -local fsmstate=self:GetState() -if self:IsAlive()then -if self.detectionOn then -self:_CheckDetectedUnits() -end -self:_UpdatePosition() -self:_CheckTurning() -local freepath=UTILS.NMToMeters(10) -if not self:IsTurning()then -freepath=self:_CheckFreePath(freepath,100) -if freepath<5000 then -if not self.collisionwarning then -self:CollisionWarning(freepath) -end -if self.pathfindingOn and not self.ispathfinding then -self.ispathfinding=self:_FindPathToNextWaypoint() -end -end -end -self:_CheckTurnsIntoWind() -self:_CheckStuck() -if self.verbose>=1 then -local nTaskTot,nTaskSched,nTaskWP=self:CountRemainingTasks() -local nMissions=self:CountRemainingMissison() -local intowind=self:IsSteamingIntoWind()and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(),true)or"N/A" -local turning=tostring(self:IsTurning()) -local alt=self.position.y -local speed=UTILS.MpsToKnots(self.velocity) -local speedExpected=UTILS.MpsToKnots(self:GetExpectedSpeed()) -local wpidxCurr=self.currentwp -local wpuidCurr=self:GetWaypointUIDFromIndex(wpidxCurr)or 0 -local wpidxNext=self:GetWaypointIndexNext()or 0 -local wpuidNext=self:GetWaypointUIDFromIndex(wpidxNext)or 0 -local wpDist=UTILS.MetersToNM(self:GetDistanceToWaypoint()or 0) -local wpETA=UTILS.SecondsToClock(self:GetTimeToWaypoint()or 0,true) -local roe=self:GetROE()or 0 -local als=self:GetAlarmstate()or 0 -local text=string.format("%s [ROE=%d,AS=%d, T/M=%d/%d]: Wp=%d[%d]-->%d[%d] (of %d) Dist=%.1f NM ETA=%s - Speed=%.1f (%.1f) kts, Depth=%.1f m, Hdg=%03d, Turn=%s Collision=%d IntoWind=%s", -fsmstate,roe,als,nTaskTot,nMissions,wpidxCurr,wpuidCurr,wpidxNext,wpuidNext,#self.waypoints or 0,wpDist,wpETA,speed,speedExpected,alt,self.heading,turning,freepath,intowind) -self:I(self.lid..text) -if false then -local text="Waypoints:" -for i,wp in pairs(self.waypoints)do -local waypoint=wp -text=text..string.format("\n%d. UID=%d",i,waypoint.uid) -if i==self.currentwp then -text=text.." current!" -end -end -env.info(text) -end -end -else -local text=string.format("State %s: Alive=%s",fsmstate,tostring(self:IsAlive())) -self:T(self.lid..text) -end -if self.verbose>=2 then -local text=string.format(self.lid.."Turn into wind time windows:") -if#self.Qintowind==0 then -text=text.." none!" -end -for i,_recovery in pairs(self.Qintowind)do -local recovery=_recovery -local Cstart=UTILS.SecondsToClock(recovery.Tstart) -local Cstop=UTILS.SecondsToClock(recovery.Tstop) -text=text..string.format("\n[%d] ID=%d Start=%s Stop=%s Open=%s Over=%s",i,recovery.Id,Cstart,Cstop,tostring(recovery.Open),tostring(recovery.Over)) -end -self:I(self.lid..text) -end -self:_PrintTaskAndMissionStatus() -self:__Status(-30) -end -function NAVYGROUP:onafterElementSpawned(From,Event,To,Element) -self:T(self.lid..string.format("Element spawned %s",Element.name)) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.SPAWNED) -end -function NAVYGROUP:onafterSpawned(From,Event,To) -self:T(self.lid..string.format("Group spawned!")) -self:_UpdatePosition() -if self.isAI then -self:SwitchROE(self.option.ROE) -self:SwitchAlarmstate(self.option.Alarm) -self:_SwitchTACAN() -self:_SwitchICLS() -if self.radioDefault then -self:SwitchRadio() -else -self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,false) -end -end -if#self.waypoints>1 then -self:Cruise() -else -self:FullStop() -end -end -function NAVYGROUP:onafterUpdateRoute(From,Event,To,n,Speed,Depth) -n=n or self:GetWaypointIndexNext() -self:_UpdateWaypointTasks(n) -local waypoints={} -local wp=UTILS.DeepCopy(self.waypoints[n]) -if Speed then -wp.speed=UTILS.KnotsToMps(Speed) -else -if self.adinfinitum and wp.speed<0.1 then -wp.speed=UTILS.KmphToMps(self.speedCruise) -end -end -if Depth then -wp.alt=-Depth -elseif self.depth then -wp.alt=-self.depth -else -end -self.speedWp=wp.speed -table.insert(waypoints,wp) -local current=self:GetCoordinate():WaypointNaval(UTILS.MpsToKmph(self.speedWp),wp.alt) -table.insert(waypoints,1,current) -if not self.passedfinalwp then -self:T(self.lid..string.format("Updateing route: WP %d-->%d (%d/%d), Speed=%.1f knots, Depth=%d m",self.currentwp,n,#waypoints,#self.waypoints,UTILS.MpsToKnots(self.speedWp),wp.alt)) -self:Route(waypoints) -else -self:E(self.lid..string.format("WARNING: Passed final WP ==> Full Stop!")) -self:FullStop() -end -end -function NAVYGROUP:onafterDetour(From,Event,To,Coordinate,Speed,Depth,ResumeRoute) -Depth=Depth or 0 -Speed=Speed or self:GetSpeedCruise() -local uid=self:GetWaypointCurrent().uid -local wp=self:AddWaypoint(Coordinate,Speed,uid,Depth,true) -if ResumeRoute then -wp.detour=1 -else -wp.detour=0 -end -end -function NAVYGROUP:onafterDetourReached(From,Event,To) -self:T(self.lid.."Group reached detour coordinate.") -end -function NAVYGROUP:onafterTurnIntoWind(From,Event,To,IntoWind) -IntoWind.Heading=self:GetHeadingIntoWind(IntoWind.Offset) -IntoWind.Open=true -IntoWind.Coordinate=self:GetCoordinate() -self.intowind=IntoWind -local _,vwind=self:GetWind() -vwind=UTILS.MpsToKnots(vwind) -local speed=math.max(IntoWind.Speed-vwind,2) -self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d",IntoWind.Heading,speed,vwind,speed+vwind,IntoWind.Tstart,IntoWind.Tstop)) -local distance=UTILS.NMToMeters(1000) -local coord=self:GetCoordinate() -local Coord=coord:Translate(distance,IntoWind.Heading) -local uid=self:GetWaypointCurrent().uid -local wptiw=self:AddWaypoint(Coord,speed,uid) -wptiw.intowind=true -IntoWind.waypoint=wptiw -if IntoWind.Uturn and self.Debug then -IntoWind.Coordinate:MarkToAll("Return coord") -end -end -function NAVYGROUP:onbeforeTurnIntoWindStop(From,Event,To) -if self.intowind then -return true -else -return false -end -end -function NAVYGROUP:onafterTurnIntoWindStop(From,Event,To) -self:TurnIntoWindOver(self.intowind) -end -function NAVYGROUP:onafterTurnIntoWindOver(From,Event,To,IntoWindData) -if IntoWindData and self.intowind and IntoWindData.Id==self.intowind.Id then -self:T2(self.lid.."Turn Into Wind Over!") -self.intowind.Over=true -self.intowind.Open=false -self:RemoveWaypointByID(self.intowind.waypoint.uid) -if self.intowind.Uturn then -self:T(self.lid.."FF Turn Into Wind Over ==> Uturn!") -self:Detour(self.intowind.Coordinate,self:GetSpeedCruise(),0,true) -else -local indx=self:GetWaypointIndexNext() -local speed=self:GetWaypointSpeed(indx) -self:T(self.lid..string.format("FF Turn Into Wind Over ==> Next WP Index=%d at %.1f knots via update route!",indx,speed)) -self:__UpdateRoute(-1,indx,speed) -end -self.intowind=nil -self:RemoveTurnIntoWind(IntoWindData) -end -end -function NAVYGROUP:onafterFullStop(From,Event,To) -self:T(self.lid.."Full stop ==> holding") -local pos=self:GetCoordinate() -local wp=pos:WaypointNaval(0) -self:Route({wp}) -end -function NAVYGROUP:onafterCruise(From,Event,To,Speed) -self.depth=nil -self:__UpdateRoute(-1,nil,Speed) -end -function NAVYGROUP:onafterDive(From,Event,To,Depth,Speed) -Depth=Depth or 50 -self:T(self.lid..string.format("Diving to %d meters",Depth)) -self.depth=Depth -self:__UpdateRoute(-1,nil,Speed) -end -function NAVYGROUP:onafterSurface(From,Event,To,Speed) -self.depth=0 -self:__UpdateRoute(-1,nil,Speed) -end -function NAVYGROUP:onafterTurningStarted(From,Event,To) -self.turning=true -end -function NAVYGROUP:onafterTurningStopped(From,Event,To) -self.turning=false -self.collisionwarning=false -if self:IsSteamingIntoWind()then -self:TurnedIntoWind() -end -end -function NAVYGROUP:onafterCollisionWarning(From,Event,To,Distance) -self:T(self.lid..string.format("Iceberg ahead in %d meters!",Distance or-1)) -self.collisionwarning=true -end -function NAVYGROUP:onafterStop(From,Event,To) -self:UnHandleEvent(EVENTS.Birth) -self:UnHandleEvent(EVENTS.Dead) -self:UnHandleEvent(EVENTS.RemoveUnit) -self:GetParent(self).onafterStop(self,From,Event,To) -end -function NAVYGROUP:OnEventBirth(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -if self.respawning then -local function reset() -self.respawning=nil -end -self:ScheduleOnce(1,reset) -else -local element=self:GetElementByName(unitname) -self:T3(self.lid..string.format("EVENT: Element %s born ==> spawned",element.name)) -self:ElementSpawned(element) -end -end -end -function NAVYGROUP:OnEventDead(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -self:T(self.lid..string.format("EVENT: Unit %s dead!",EventData.IniUnitName)) -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed",element.name)) -self:ElementDestroyed(element) -end -end -end -function NAVYGROUP:OnEventRemoveUnit(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -self:T(self.lid..string.format("EVENT: Element %s removed ==> dead",element.name)) -self:ElementDead(element) -end -end -end -function NAVYGROUP:AddWaypoint(Coordinate,Speed,AfterWaypointWithID,Depth,Updateroute) -if not Coordinate:IsInstanceOf("COORDINATE")then -if Coordinate:IsInstanceOf("POSITIONABLE")or Coordinate:IsInstanceOf("ZONE_BASE")then -self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate") -Coordinate=Coordinate:GetCoordinate() -else -self:E(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!") -return nil -end -end -local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) -if wpnumber>self.currentwp then -self.passedfinalwp=false -end -Speed=Speed or self:GetSpeedCruise() -local wp=Coordinate:WaypointNaval(UTILS.KnotsToKmph(Speed),Depth) -local waypoint=self:_CreateWaypoint(wp) -self:_AddWaypoint(waypoint,wpnumber) -self:T(self.lid..string.format("Adding NAVAL waypoint index=%d uid=%d, speed=%.1f knots. Last waypoint passed was #%d. Total waypoints #%d",wpnumber,waypoint.uid,Speed,self.currentwp,#self.waypoints)) -if Updateroute==nil or Updateroute==true then -self:_CheckGroupDone(1) -end -return waypoint -end -function NAVYGROUP:_InitGroup() -if self.groupinitialized then -self:E(self.lid.."WARNING: Group was already initialized!") -return -end -self.template=self.group:GetTemplate() -self.isAircraft=false -self.isNaval=true -self.isGround=false -self.isAI=true -self.isLateActivated=self.template.lateActivation -self.isUncontrolled=false -self.speedMax=self.group:GetSpeedMax() -self.speedCruise=self.speedMax*0.7 -self.ammo=self:GetAmmoTot() -self.radio.On=true -self.radio.Freq=tonumber(self.template.units[1].frequency)/1000000 -self.radio.Modu=tonumber(self.template.units[1].modulation) -self.optionDefault.Formation="Off Road" -self.option.Formation=self.optionDefault.Formation -self:SetDefaultTACAN(nil,nil,nil,nil,true) -self.tacan=UTILS.DeepCopy(self.tacanDefault) -self:SetDefaultICLS(nil,nil,nil,true) -self.icls=UTILS.DeepCopy(self.iclsDefault) -local units=self.group:GetUnits() -for _,_unit in pairs(units)do -local unit=_unit -local unittemplate=unit:GetTemplate() -local element={} -element.name=unit:GetName() -element.unit=unit -element.status=OPSGROUP.ElementStatus.INUTERO -element.typename=unit:GetTypeName() -element.skill=unittemplate.skill or"Unknown" -element.ai=true -element.category=element.unit:GetUnitCategory() -element.categoryname=element.unit:GetCategoryName() -element.size,element.length,element.height,element.width=unit:GetObjectSize() -element.ammo0=self:GetAmmoUnit(unit,false) -if self.verbose>=2 then -local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", -element.name,element.status,element.skill,element.categoryname,element.category,element.size,element.length,element.height,element.width) -self:I(self.lid..text) -end -table.insert(self.elements,element) -self.descriptors=self.descriptors or unit:GetDesc() -self.actype=self.actype or unit:GetTypeName() -if unit:IsAlive()then -self:ElementSpawned(element) -end -end -if self.verbose>=1 then -local text=string.format("Initialized Navy Group %s:\n",self.groupname) -text=text..string.format("Unit type = %s\n",self.actype) -text=text..string.format("Speed max = %.1f Knots\n",UTILS.KmphToKnots(self.speedMax)) -text=text..string.format("Speed cruise = %.1f Knots\n",UTILS.KmphToKnots(self.speedCruise)) -text=text..string.format("Elements = %d\n",#self.elements) -text=text..string.format("Waypoints = %d\n",#self.waypoints) -text=text..string.format("Radio = %.1f MHz %s %s\n",self.radio.Freq,UTILS.GetModulationName(self.radio.Modu),tostring(self.radio.On)) -text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n",self.ammo.Total,self.ammo.Guns,self.ammo.Rockets,self.ammo.Missiles,self.ammo.Torpedos) -text=text..string.format("FSM state = %s\n",self:GetState()) -text=text..string.format("Is alive = %s\n",tostring(self:IsAlive())) -text=text..string.format("LateActivate = %s\n",tostring(self:IsLateActivated())) -self:I(self.lid..text) -end -self.groupinitialized=true -return self -end -function NAVYGROUP:_CheckFreePath(DistanceMax,dx) -local distance=DistanceMax or 5000 -local dx=dx or 100 -if self:IsTurning()then -return distance -end -local offsetY=0.1 -if UTILS.GetDCSMap()==DCSMAP.Caucasus then -offsetY=5.01 -end -local vec3=self:GetVec3() -vec3.y=offsetY -local heading=self:GetHeading() -local function LoS(dist) -local checkvec3=UTILS.VecTranslate(vec3,dist,heading) -local los=land.isVisible(vec3,checkvec3) -return los -end -if LoS(DistanceMax)then -return DistanceMax -end -local function check() -local xmin=0 -local xmax=DistanceMax -local Nmax=100 -local eps=100 -local N=1 -while N<=Nmax do -local d=xmax-xmin -local x=xmin+d/2 -local los=LoS(x) -self:T2(self.lid..string.format("N=%d: xmin=%.1f xmax=%.1f x=%.1f d=%.3f los=%s",N,xmin,xmax,x,d,tostring(los))) -if los and d<=eps then -return x -end -if los then -xmin=x -else -xmax=x -end -N=N+1 -end -return 0 -end -return check() -end -function NAVYGROUP:_CheckTurning() -local unit=self.group:GetUnit(1) -if unit and unit:IsAlive()then -local vNew=self.orientX -local vLast=self.orientXLast -vNew.y=0;vLast.y=0 -local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast))) -local turning=math.abs(deltaLast)>=2 -if self.turning and not turning then -self:TurningStopped() -elseif turning and not self.turning then -self:TurningStarted() -end -self.turning=turning -end -end -function NAVYGROUP:_CheckTurnsIntoWind() -local time=timer.getAbsTime() -if self.intowind then -if time>=self.intowind.Tstop then -self:TurnIntoWindOver(self.intowind) -end -else -local IntoWind=self:GetTurnIntoWindNext() -if IntoWind then -self:TurnIntoWind(IntoWind) -end -end -end -function NAVYGROUP:GetTurnIntoWindNext() -if#self.Qintowind>0 then -local time=timer.getAbsTime() -table.sort(self.Qintowind,function(a,b)return a.Tstart=recovery.Tstart and time0 -else -return false -end -end -return findpath() -end -ARMYGROUP={ -ClassName="ARMYGROUP", -formationPerma=nil, -engage={}, -} -ARMYGROUP.version="0.4.0" -function ARMYGROUP:New(Group) -local self=BASE:Inherit(self,OPSGROUP:New(Group)) -self.lid=string.format("ARMYGROUP %s | ",self.groupname) -self.isArmygroup=true -self:SetDefaultROE() -self:SetDefaultAlarmstate() -self:SetDetection() -self:SetPatrolAdInfinitum(false) -self:SetRetreatZones() -self:AddTransition("*","FullStop","Holding") -self:AddTransition("*","Cruise","Cruising") -self:AddTransition("*","Detour","OnDetour") -self:AddTransition("OnDetour","DetourReached","Cruising") -self:AddTransition("*","Retreat","Retreating") -self:AddTransition("Retreating","Retreated","Retreated") -self:AddTransition("Cruising","EngageTarget","Engaging") -self:AddTransition("Holding","EngageTarget","Engaging") -self:AddTransition("OnDetour","EngageTarget","Engaging") -self:AddTransition("Engaging","Disengage","Cruising") -self:AddTransition("*","Rearm","Rearm") -self:AddTransition("Rearm","Rearming","Rearming") -self:AddTransition("Rearming","Rearmed","Cruising") -self:InitWaypoints() -self:_InitGroup() -self:HandleEvent(EVENTS.Birth,self.OnEventBirth) -self:HandleEvent(EVENTS.Dead,self.OnEventDead) -self:HandleEvent(EVENTS.RemoveUnit,self.OnEventRemoveUnit) -self:__Status(-1) -self.timerQueueUpdate=TIMER:New(self._QueueUpdate,self):Start(2,5) -self.timerCheckZone=TIMER:New(self._CheckInZones,self):Start(2,30) -return self -end -function ARMYGROUP:SetPatrolAdInfinitum(switch) -if switch==false then -self.adinfinitum=false -else -self.adinfinitum=true -end -return self -end -function ARMYGROUP:GetClosestRoad() -return self:GetCoordinate():GetClosestPointToRoad() -end -function ARMYGROUP:GetClosestRoadDist() -local road=self:GetClosestRoad() -if road then -local dist=road:Get2DDistance(self:GetCoordinate()) -return dist -end -return math.huge -end -function ARMYGROUP:AddTaskFireAtPoint(Coordinate,Clock,Radius,Nshots,WeaponType,Prio) -Coordinate=self:_CoordinateFromObject(Coordinate) -local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,Coordinate:GetVec2(),Radius,Nshots,WeaponType) -local task=self:AddTask(DCStask,Clock,nil,Prio) -return task -end -function ARMYGROUP:AddTaskWaypointFireAtPoint(Coordinate,Waypoint,Radius,Nshots,WeaponType,Prio) -Coordinate=self:_CoordinateFromObject(Coordinate) -Waypoint=Waypoint or self:GetWaypointNext() -local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,Coordinate:GetVec2(),Radius,Nshots,WeaponType) -local task=self:AddTaskWaypoint(DCStask,Waypoint,nil,Prio) -return task -end -function ARMYGROUP:AddTaskAttackGroup(TargetGroup,WeaponExpend,WeaponType,Clock,Prio) -local DCStask=CONTROLLABLE.TaskAttackGroup(nil,TargetGroup,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit,GroupAttack) -local task=self:AddTask(DCStask,Clock,nil,Prio) -return task -end -function ARMYGROUP:SetRetreatZones(RetreatZoneSet) -self.retreatZones=RetreatZoneSet or SET_ZONE:New() -return self -end -function ARMYGROUP:AddRetreatZone(RetreatZone) -self.retreatZones:AddZone(RetreatZone) -return self -end -function ARMYGROUP:IsHolding() -return self:Is("Holding") -end -function ARMYGROUP:IsCruising() -return self:Is("Cruising") -end -function ARMYGROUP:IsOnDetour() -return self:Is("OnDetour") -end -function ARMYGROUP:IsCombatReady() -local combatready=true -if self:IsRearming()or self:IsRetreating()or self.outofAmmo or self:IsEngaging()or self:is("Retreated")or self:IsDead()or self:IsStopped()or self:IsInUtero()then -combatready=false -end -return combatready -end -function ARMYGROUP:onbeforeStatus(From,Event,To) -if self:IsDead()then -self:T(self.lid..string.format("Onbefore Status DEAD ==> false")) -return false -elseif self:IsStopped()then -self:T(self.lid..string.format("Onbefore Status STOPPED ==> false")) -return false -end -return true -end -function ARMYGROUP:onafterStatus(From,Event,To) -local fsmstate=self:GetState() -if self:IsAlive()then -if self.detectionOn then -self:_CheckDetectedUnits() -end -self:_CheckAmmoStatus() -self:_UpdatePosition() -self:_CheckStuck() -self:_CheckDamage() -if self:IsEngaging()then -self:_UpdateEngageTarget() -end -if self.verbose>=1 then -local nTaskTot,nTaskSched,nTaskWP=self:CountRemainingTasks() -local nMissions=self:CountRemainingMissison() -local roe=self:GetROE() -local alarm=self:GetAlarmstate() -local speed=UTILS.MpsToKnots(self.velocity) -local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed()) -local formation=self.option.Formation or"unknown" -local ammo=self:GetAmmoTot() -local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Life=%.1f, Speed=%.1f (%d), Heading=%03d, Ammo=%d", -fsmstate,roe,alarm,nTaskTot,nMissions,self.currentwp,#self.waypoints,self:GetWaypointIndexNext(),tostring(self.passedfinalwp),self.life or 0,speed,speedEx,self.heading,ammo.Total) -self:I(self.lid..text) -end -else -local text=string.format("State %s: Alive=%s",fsmstate,tostring(self:IsAlive())) -self:T2(self.lid..text) -end -self:_PrintTaskAndMissionStatus() -self:__Status(-30) -end -function ARMYGROUP:onafterElementSpawned(From,Event,To,Element) -self:T(self.lid..string.format("Element spawned %s",Element.name)) -self:_UpdateStatus(Element,OPSGROUP.ElementStatus.SPAWNED) -end -function ARMYGROUP:onafterSpawned(From,Event,To) -self:T(self.lid..string.format("Group spawned!")) -self:_UpdatePosition() -if self.isAI then -self:SwitchROE(self.option.ROE) -self:SwitchAlarmstate(self.option.Alarm) -self:_SwitchTACAN() -if self.radioDefault then -self:SwitchRadio(self.radioDefault.Freq,self.radioDefault.Modu) -else -self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,true) -end -if not self.option.Formation then -self.option.Formation=self.optionDefault.Formation -end -end -if#self.waypoints>1 then -self:Cruise(nil,self.option.Formation or self.optionDefault.Formation) -else -self:FullStop() -end -end -function ARMYGROUP:onafterUpdateRoute(From,Event,To,n,Speed,Formation) -local text=string.format("Update route n=%s, Speed=%s, Formation=%s",tostring(n),tostring(Speed),tostring(Formation)) -self:T(self.lid..text) -n=n or self:GetWaypointIndexNext(self.adinfinitum) -self:_UpdateWaypointTasks(n) -local waypoints={} -local wp=UTILS.DeepCopy(self.waypoints[n]) -local onroad=wp.action==ENUMS.Formation.Vehicle.OnRoad -if Speed then -wp.speed=UTILS.KnotsToMps(Speed) -else -if self.adinfinitum and wp.speed<0.1 then -wp.speed=UTILS.KmphToMps(self.speedCruise) -end -end -if self.formationPerma then -wp.action=self.formationPerma -elseif Formation then -wp.action=Formation -end -self.option.Formation=wp.action -self.speedWp=wp.speed -if onroad then -wp.action=ENUMS.Formation.Vehicle.OffRoad -local wproad=wp.roadcoord:WaypointGround(wp.speed,ENUMS.Formation.Vehicle.OnRoad) -table.insert(waypoints,wproad) -end -table.insert(waypoints,wp) -local formation=ENUMS.Formation.Vehicle.OffRoad -if wp.action~=ENUMS.Formation.Vehicle.OnRoad then -formation=wp.action -end -local current=self:GetCoordinate():WaypointGround(UTILS.MpsToKmph(self.speedWp),formation) -table.insert(waypoints,1,current) -if onroad then -local current=self:GetClosestRoad():WaypointGround(UTILS.MpsToKmph(self.speedWp),ENUMS.Formation.Vehicle.OnRoad) -table.insert(waypoints,2,current) -end -if false then -for i,_wp in pairs(waypoints)do -local wp=_wp -local text=string.format("WP #%d UID=%d type=%s: Speed=%d m/s, alt=%d m, Action=%s",i,wp.uid and wp.uid or 0,wp.type,wp.speed,wp.alt,wp.action) -self:T(text) -end -end -if self:IsEngaging()or not self.passedfinalwp then -self:T(self.lid..string.format("Updateing route: WP %d-->%d (%d/%d), Speed=%.1f knots, Formation=%s", -self.currentwp,n,#waypoints,#self.waypoints,UTILS.MpsToKnots(self.speedWp),tostring(self.option.Formation))) -self:Route(waypoints) -else -self:E(self.lid..string.format("WARNING: Passed final WP ==> Full Stop!")) -self:FullStop() -end -end -function ARMYGROUP:onafterGotoWaypoint(From,Event,To,UID,Speed,Formation) -local n=self:GetWaypointIndex(UID) -if n then -if false then -local tasks=self:GetTasksWaypoint(n) -for _,_task in pairs(tasks)do -local task=_task -task.status=OPSGROUP.TaskStatus.SCHEDULED -end -end -Speed=Speed or self:GetSpeedToWaypoint(n) -self:UpdateRoute(n,Speed,Formation) -end -end -function ARMYGROUP:onafterDetour(From,Event,To,Coordinate,Speed,Formation,ResumeRoute) -for _,_wp in pairs(self.waypoints)do -local wp=_wp -if wp.detour then -self:RemoveWaypointByID(wp.uid) -end -end -Speed=Speed or self:GetSpeedCruise() -local uid=self:GetWaypointCurrent().uid -local wp=self:AddWaypoint(Coordinate,Speed,uid,Formation,true) -if ResumeRoute then -wp.detour=1 -else -wp.detour=0 -end -end -function ARMYGROUP:onafterRearm(From,Event,To,Coordinate,Formation) -local uid=self:GetWaypointCurrent().uid -local wp=self:AddWaypoint(Coordinate,nil,uid,Formation,true) -wp.detour=0 -end -function ARMYGROUP:onafterRearming(From,Event,To) -local pos=self:GetCoordinate() -local wp=pos:WaypointGround(0) -self:Route({wp}) -end -function ARMYGROUP:onbeforeRetreat(From,Event,To,Zone,Formation) -if not Zone then -local a=self:GetVec2() -local distmin=math.huge -local zonemin=nil -for _,_zone in pairs(self.retreatZones:GetSet())do -local zone=_zone -local b=zone:GetVec2() -local dist=UTILS.VecDist2D(a,b) -if dist100 then -self.engage.Coordinate:UpdateFromVec3(vec3) -local uid=self:GetWaypointCurrent().uid -self:RemoveWaypointByID(self.engage.Waypoint.uid) -self.engage.Waypoint=self:AddWaypoint(self.engage.Coordinate,nil,uid,Formation,true) -self.engage.Waypoint.detour=0 -end -else -self:Disengage() -end -end -function ARMYGROUP:onafterDisengage(From,Event,To) -self:_CheckGroupDone(1) -end -function ARMYGROUP:onafterRearmed(From,Event,To) -self:_CheckGroupDone(1) -end -function ARMYGROUP:onafterDetourReached(From,Event,To) -self:I(self.lid.."Group reached detour coordinate.") -end -function ARMYGROUP:onafterFullStop(From,Event,To) -local pos=self:GetCoordinate() -local wp=pos:WaypointGround(0) -self:Route({wp}) -end -function ARMYGROUP:onafterCruise(From,Event,To,Speed,Formation) -self:__UpdateRoute(-1,nil,Speed,Formation) -end -function ARMYGROUP:onafterStop(From,Event,To) -self:UnHandleEvent(EVENTS.Birth) -self:UnHandleEvent(EVENTS.Dead) -self:UnHandleEvent(EVENTS.RemoveUnit) -self:GetParent(self).onafterStop(self,From,Event,To) -end -function ARMYGROUP:OnEventBirth(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -if self.respawning then -local function reset() -self.respawning=nil -end -self:ScheduleOnce(1,reset) -else -local element=self:GetElementByName(unitname) -self:T3(self.lid..string.format("EVENT: Element %s born ==> spawned",element.name)) -self:ElementSpawned(element) -end -end -end -function ARMYGROUP:OnEventDead(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -self:T(self.lid..string.format("EVENT: Unit %s dead!",EventData.IniUnitName)) -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed",element.name)) -self:ElementDestroyed(element) -end -end -end -function ARMYGROUP:OnEventRemoveUnit(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -local element=self:GetElementByName(unitname) -if element then -self:T(self.lid..string.format("EVENT: Element %s removed ==> dead",element.name)) -self:ElementDead(element) -end -end -end -function ARMYGROUP:OnEventHit(EventData) -if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then -local unit=EventData.IniUnit -local group=EventData.IniGroup -local unitname=EventData.IniUnitName -end -end -function ARMYGROUP:AddWaypoint(Coordinate,Speed,AfterWaypointWithID,Formation,Updateroute) -local coordinate=self:_CoordinateFromObject(Coordinate) -local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) -if wpnumber>self.currentwp then -self.passedfinalwp=false -end -Speed=Speed or self:GetSpeedCruise() -local wp=coordinate:WaypointGround(UTILS.KnotsToKmph(Speed),Formation) -local waypoint=self:_CreateWaypoint(wp) -self:_AddWaypoint(waypoint,wpnumber) -waypoint.roadcoord=coordinate:GetClosestPointToRoad(false) -if waypoint.roadcoord then -waypoint.roaddist=coordinate:Get2DDistance(waypoint.roadcoord) -else -waypoint.roaddist=1000*1000 -end -self:T(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s",waypoint.uid,wpnumber,Speed,waypoint.roaddist,waypoint.action)) -if Updateroute==nil or Updateroute==true then -self:_CheckGroupDone(1) -end -return waypoint -end -function ARMYGROUP:_InitGroup() -if self.groupinitialized then -self:E(self.lid.."WARNING: Group was already initialized!") -return -end -self.template=self.group:GetTemplate() -self.isAircraft=false -self.isNaval=false -self.isGround=true -self.isAI=true -self.isLateActivated=self.template.lateActivation -self.isUncontrolled=false -self.speedMax=self.group:GetSpeedMax() -self.speedCruise=self.speedMax*0.7 -self.ammo=self:GetAmmoTot() -self.radio.On=false -self.radio.Freq=133 -self.radio.Modu=radio.modulation.AM -self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,self.radio.On) -self.optionDefault.Formation=self:GetWaypoint(1).action -self:SetDefaultTACAN(nil,nil,nil,nil,true) -self.tacan=UTILS.DeepCopy(self.tacanDefault) -local units=self.group:GetUnits() -for _,_unit in pairs(units)do -local unit=_unit -local unittemplate=unit:GetTemplate() -local element={} -element.name=unit:GetName() -element.unit=unit -element.status=OPSGROUP.ElementStatus.INUTERO -element.typename=unit:GetTypeName() -element.skill=unittemplate.skill or"Unknown" -element.ai=true -element.category=element.unit:GetUnitCategory() -element.categoryname=element.unit:GetCategoryName() -element.size,element.length,element.height,element.width=unit:GetObjectSize() -element.ammo0=self:GetAmmoUnit(unit,false) -element.life0=unit:GetLife0() -element.life=element.life0 -if self.verbose>=2 then -local text=string.format("Adding element %s: status=%s, skill=%s, life=%.3f category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", -element.name,element.status,element.skill,element.life,element.categoryname,element.category,element.size,element.length,element.height,element.width) -self:I(self.lid..text) -end -table.insert(self.elements,element) -self.descriptors=self.descriptors or unit:GetDesc() -self.actype=self.actype or unit:GetTypeName() -if unit:IsAlive()then -self:ElementSpawned(element) -end -end -if self.verbose>=1 then -local text=string.format("Initialized Army Group %s:\n",self.groupname) -text=text..string.format("Unit type = %s\n",self.actype) -text=text..string.format("Speed max = %.1f Knots\n",UTILS.KmphToKnots(self.speedMax)) -text=text..string.format("Speed cruise = %.1f Knots\n",UTILS.KmphToKnots(self.speedCruise)) -text=text..string.format("Elements = %d\n",#self.elements) -text=text..string.format("Waypoints = %d\n",#self.waypoints) -text=text..string.format("Radio = %.1f MHz %s %s\n",self.radio.Freq,UTILS.GetModulationName(self.radio.Modu),tostring(self.radio.On)) -text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n",self.ammo.Total,self.ammo.Guns,self.ammo.Rockets,self.ammo.Missiles) -text=text..string.format("FSM state = %s\n",self:GetState()) -text=text..string.format("Is alive = %s\n",tostring(self:IsAlive())) -text=text..string.format("LateActivate = %s\n",tostring(self:IsLateActivated())) -self:I(self.lid..text) -end -self.groupinitialized=true -return self -end -function ARMYGROUP:SwitchFormation(Formation,Permanently,NoRouteUpdate) -if self:IsAlive()or self:IsInUtero()then -Formation=Formation or self.optionDefault.Formation -if Permanently then -self.formationPerma=Formation -else -self.formationPerma=nil -end -self.option.Formation=Formation -if self:IsInUtero()then -self:T(self.lid..string.format("Will switch formation to %s (permanently=%s) when group is spawned",self.option.Formation,tostring(Permanently))) -else -if NoRouteUpdate then -else -self:__UpdateRoute(-1,nil,nil,Formation) -end -self:T(self.lid..string.format("Switching formation to %s (permanently=%s)",self.option.Formation,tostring(Permanently))) -end -end -return self -end -SQUADRON={ -ClassName="SQUADRON", -verbose=0, -lid=nil, -name=nil, -templatename=nil, -aircrafttype=nil, -assets={}, -missiontypes={}, -repairtime=0, -maintenancetime=0, -livery=nil, -skill=nil, -modex=nil, -modexcounter=0, -callsignName=nil, -callsigncounter=11, -airwing=nil, -Ngroups=nil, -engageRange=nil, -tankerSystem=nil, -refuelSystem=nil, -tacanChannel={}, -} -SQUADRON.version="0.5.0" -function SQUADRON:New(TemplateGroupName,Ngroups,SquadronName) -local self=BASE:Inherit(self,FSM:New()) -self.templatename=TemplateGroupName -self.name=tostring(SquadronName or TemplateGroupName) -self.lid=string.format("SQUADRON %s | ",self.name) -self.templategroup=GROUP:FindByName(self.templatename) -if not self.templategroup then -self:E(self.lid..string.format("ERROR: Template group %s does not exist!",tostring(self.templatename))) -return nil -end -self.Ngroups=Ngroups or 3 -self:SetMissionRange() -self:SetSkill(AI.Skill.GOOD) -self:AddMissionCapability(AUFTRAG.Type.ORBIT) -self.attribute=self.templategroup:GetAttribute() -self.aircrafttype=self.templategroup:GetTypeName() -self.refuelSystem=select(2,self.templategroup:GetUnit(1):IsRefuelable()) -self.tankerSystem=select(2,self.templategroup:GetUnit(1):IsTanker()) -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","OnDuty") -self:AddTransition("*","Status","*") -self:AddTransition("OnDuty","Pause","Paused") -self:AddTransition("Paused","Unpause","OnDuty") -self:AddTransition("*","Stop","Stopped") -if false then -BASE:TraceOnOff(true) -BASE:TraceClass(self.ClassName) -BASE:TraceLevel(1) -end -return self -end -function SQUADRON:SetLivery(LiveryName) -self.livery=LiveryName -return self -end -function SQUADRON:SetSkill(Skill) -self.skill=Skill -return self -end -function SQUADRON:SetVerbosity(VerbosityLevel) -self.verbose=VerbosityLevel or 0 -return self -end -function SQUADRON:SetTurnoverTime(MaintenanceTime,RepairTime) -self.maintenancetime=MaintenanceTime and MaintenanceTime*60 or 0 -self.repairtime=RepairTime and RepairTime*60 or 0 -return self -end -function SQUADRON:SetRadio(Frequency,Modulation) -self.radioFreq=Frequency or 251 -self.radioModu=Modulation or radio.modulation.AM -return self -end -function SQUADRON:SetGrouping(nunits) -self.ngrouping=nunits or 2 -if self.ngrouping<1 then self.ngrouping=1 end -if self.ngrouping>4 then self.ngrouping=4 end -return self -end -function SQUADRON:AddMissionCapability(MissionTypes,Performance) -if MissionTypes and type(MissionTypes)~="table"then -MissionTypes={MissionTypes} -end -self.missiontypes=self.missiontypes or{} -for _,missiontype in pairs(MissionTypes)do -if self:CheckMissionCapability(missiontype,self.missiontypes)then -self:E(self.lid.."WARNING: Mission capability already present! No need to add it twice.") -else -local capability={} -capability.MissionType=missiontype -capability.Performance=Performance or 50 -table.insert(self.missiontypes,capability) -end -end -self:T2(self.missiontypes) -return self -end -function SQUADRON:GetMissionTypes() -local missiontypes={} -for _,Capability in pairs(self.missiontypes)do -local capability=Capability -table.insert(missiontypes,capability.MissionType) -end -return missiontypes -end -function SQUADRON:GetMissionCapabilities() -return self.missiontypes -end -function SQUADRON:GetMissionPeformance(MissionType) -for _,Capability in pairs(self.missiontypes)do -local capability=Capability -if capability.MissionType==MissionType then -return capability.Performance -end -end -return-1 -end -function SQUADRON:SetMissionRange(Range) -self.engageRange=UTILS.NMToMeters(Range or 100) -return self -end -function SQUADRON:SetCallsign(Callsign,Index) -self.callsignName=Callsign -self.callsignIndex=Index -return self -end -function SQUADRON:SetModex(Modex,Prefix,Suffix) -self.modex=Modex -self.modexPrefix=Prefix -self.modexSuffix=Suffix -return self -end -function SQUADRON:SetFuelLowThreshold(LowFuel) -self.fuellow=LowFuel or 25 -return self -end -function SQUADRON:SetFuelLowRefuel(switch) -if switch==false then -self.fuellowRefuel=false -else -self.fuellowRefuel=true -end -return self -end -function SQUADRON:SetAirwing(Airwing) -self.airwing=Airwing -return self -end -function SQUADRON:AddAsset(Asset) -self:T(self.lid..string.format("Adding asset %s of type %s",Asset.spawngroupname,Asset.unittype)) -Asset.squadname=self.name -table.insert(self.assets,Asset) -return self -end -function SQUADRON:DelAsset(Asset) -for i,_asset in pairs(self.assets)do -local asset=_asset -if Asset.uid==asset.uid then -self:T2(self.lid..string.format("Removing asset %s",asset.spawngroupname)) -table.remove(self.assets,i) -break -end -end -return self -end -function SQUADRON:DelGroup(GroupName) -for i,_asset in pairs(self.assets)do -local asset=_asset -if GroupName==asset.spawngroupname then -self:T2(self.lid..string.format("Removing asset %s",asset.spawngroupname)) -table.remove(self.assets,i) -break -end -end -return self -end -function SQUADRON:GetName() -return self.name -end -function SQUADRON:GetRadio() -return self.radioFreq,self.radioModu -end -function SQUADRON:GetCallsign(Asset) -if self.callsignName then -Asset.callsign={} -for i=1,Asset.nunits do -local callsign={} -callsign[1]=self.callsignName -callsign[2]=math.floor(self.callsigncounter/10) -callsign[3]=self.callsigncounter%10 -if callsign[3]==0 then -callsign[3]=1 -self.callsigncounter=self.callsigncounter+2 -else -self.callsigncounter=self.callsigncounter+1 -end -Asset.callsign[i]=callsign -self:T3({callsign=callsign}) -end -end -end -function SQUADRON:GetModex(Asset) -if self.modex then -Asset.modex={} -for i=1,Asset.nunits do -Asset.modex[i]=string.format("%03d",self.modex+self.modexcounter) -self.modexcounter=self.modexcounter+1 -self:T3({modex=Asset.modex[i]}) -end -end -end -function SQUADRON:AddTacanChannel(ChannelMin,ChannelMax) -ChannelMax=ChannelMax or ChannelMin -if ChannelMin>126 then -self:E(self.lid.."ERROR: TACAN Channel must be <= 126! Will not add to available channels") -return self -end -if ChannelMax>126 then -self:E(self.lid.."WARNING: TACAN Channel must be <= 126! Adjusting ChannelMax to 126") -ChannelMax=126 -end -for i=ChannelMin,ChannelMax do -self.tacanChannel[i]=true -end -return self -end -function SQUADRON:FetchTacan() -for channel,free in pairs(self.tacanChannel)do -if free then -self:T(self.lid..string.format("Checking out Tacan channel %d",channel)) -self.tacanChannel[channel]=false -return channel -end -end -return nil -end -function SQUADRON:ReturnTacan(channel) -self:T(self.lid..string.format("Returning Tacan channel %d",channel)) -self.tacanChannel[channel]=true -end -function SQUADRON:IsOnDuty() -return self:Is("OnDuty") -end -function SQUADRON:IsStopped() -return self:Is("Stopped") -end -function SQUADRON:IsPaused() -return self:Is("Paused") -end -function SQUADRON:onafterStart(From,Event,To) -local text=string.format("Starting SQUADRON",self.name) -self:T(self.lid..text) -self:__Status(-1) -end -function SQUADRON:onafterStatus(From,Event,To) -if self.verbose>=1 then -local fsmstate=self:GetState() -local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName)or"N/A" -local modex=self.modex and self.modex or-1 -local skill=self.skill and tostring(self.skill)or"N/A" -local NassetsTot=#self.assets -local NassetsInS=self:CountAssetsInStock() -local NassetsQP=0;local NassetsP=0;local NassetsQ=0 -if self.airwing then -NassetsQP,NassetsP,NassetsQ=self.airwing:CountAssetsOnMission(nil,self) -end -local text=string.format("%s [Type=%s, Call=%s, Modex=%d, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]", -fsmstate,self.aircrafttype,callsign,modex,skill,NassetsTot,NassetsInS,NassetsQP,NassetsP,NassetsQ) -self:I(self.lid..text) -self:_CheckAssetStatus() -end -if not self:IsStopped()then -self:__Status(-60) -end -end -function SQUADRON:_CheckAssetStatus() -if self.verbose>=2 and#self.assets>0 then -local text="" -for j,_asset in pairs(self.assets)do -local asset=_asset -text=text..string.format("\n[%d] %s (%s*%d): ",j,asset.spawngroupname,asset.unittype,asset.nunits) -if asset.spawned then -local mission=self.airwing and self.airwing:GetAssetCurrentMission(asset)or false -if mission then -local distance=asset.flightgroup and UTILS.MetersToNM(mission:GetTargetDistance(asset.flightgroup.group:GetCoordinate()))or 0 -text=text..string.format("Mission %s - %s: Status=%s, Dist=%.1f NM",mission.name,mission.type,mission.status,distance) -else -text=text.."Mission None" -end -text=text..", Flight: " -if asset.flightgroup and asset.flightgroup:IsAlive()then -local status=asset.flightgroup:GetState() -local fuelmin=asset.flightgroup:GetFuelMin() -local fuellow=asset.flightgroup:IsFuelLow() -local fuelcri=asset.flightgroup:IsFuelCritical() -text=text..string.format("%s Fuel=%d",status,fuelmin) -if fuelcri then -text=text.." (Critical!)" -elseif fuellow then -text=text.." (Low)" -end -local lifept,lifept0=asset.flightgroup:GetLifePoints() -text=text..string.format(", Life=%d/%d",lifept,lifept0) -local ammo=asset.flightgroup:GetAmmoTot() -text=text..string.format(", Ammo=%d [G=%d, R=%d, B=%d, M=%d]",ammo.Total,ammo.Guns,ammo.Rockets,ammo.Bombs,ammo.Missiles) -else -text=text.."N/A" -end -local payload=asset.payload and table.concat(self.airwing:GetPayloadMissionTypes(asset.payload),", ")or"None" -text=text..", Payload={"..payload.."}" -else -text=text..string.format("In Stock") -if self:IsRepaired(asset)then -text=text..", Combat Ready" -else -text=text..string.format(", Repaired in %d sec",self:GetRepairTime(asset)) -if asset.damage then -text=text..string.format(" (Damage=%.1f)",asset.damage) -end -end -if asset.Treturned then -local T=timer.getAbsTime()-asset.Treturned -text=text..string.format(", Returned for %d sec",T) -end -end -end -self:I(self.lid..text) -end -end -function SQUADRON:onafterStop(From,Event,To) -self:I(self.lid.."STOPPING Squadron!") -for i=#self.assets,1,-1 do -local asset=self.assets[i] -self:DelAsset(asset) -end -self.CallScheduler:Clear() -end -function SQUADRON:CanMission(Mission) -local cando=true -if not self:IsOnDuty()then -self:T(self.lid..string.format("Squad in not OnDuty but in state %s. Cannot do mission %s with target %s",self:GetState(),Mission.name,Mission:GetTargetName())) -return false -end -if not self:CheckMissionType(Mission.type,self:GetMissionTypes())then -self:T(self.lid..string.format("INFO: Squad cannot do mission type %s (%s, %s)",Mission.type,Mission.name,Mission:GetTargetName())) -return false -end -if Mission.type==AUFTRAG.Type.TANKER then -if Mission.refuelSystem and Mission.refuelSystem==self.tankerSystem then -else -self:T(self.lid..string.format("INFO: Wrong refueling system requested=%s != %s=available",tostring(Mission.refuelSystem),tostring(self.tankerSystem))) -return false -end -end -local TargetDistance=Mission:GetTargetDistance(self.airwing:GetCoordinate()) -local engagerange=Mission.engageRange and math.max(self.engageRange,Mission.engageRange)or self.engageRange -if TargetDistance>engagerange then -self:I(self.lid..string.format("INFO: Squad is not in range. Target dist=%d > %d NM max mission Range",UTILS.MetersToNM(TargetDistance),UTILS.MetersToNM(engagerange))) -return false -end -return true -end -function SQUADRON:CountAssetsInStock() -local N=0 -for _,_asset in pairs(self.assets)do -local asset=_asset -if asset.spawned then -else -N=N+1 -end -end -return N -end -function SQUADRON:RecruitAssets(Mission,Npayloads) -Npayloads=Npayloads or self.airwing:CountPayloadsInStock(Mission.type,self.aircrafttype,Mission.payloads) -local assets={} -for _,_asset in pairs(self.assets)do -local asset=_asset -if self.airwing:IsAssetOnMission(asset)then -if self.airwing:IsAssetOnMission(asset,AUFTRAG.Type.GCICAP)and Mission.type==AUFTRAG.Type.INTERCEPT then -self:I(self.lid.."Adding asset on GCICAP mission for an INTERCEPT mission") -table.insert(assets,asset) -end -else -if asset.spawned then -local flightgroup=asset.flightgroup -if self:CheckMissionCapability(Mission.type,asset.payload.capabilities)and flightgroup and flightgroup:IsAlive()then -local combatready=true -if Mission.type==AUFTRAG.Type.INTERCEPT then -combatready=flightgroup:CanAirToAir() -else -local excludeguns=Mission.type==AUFTRAG.Type.BOMBING or Mission.type==AUFTRAG.Type.BOMBRUNWAY or Mission.type==AUFTRAG.Type.BOMBCARPET or Mission.type==AUFTRAG.Type.SEAD or Mission.type==AUFTRAG.Type.ANTISHIP -combatready=flightgroup:CanAirToGround(excludeguns) -end -if flightgroup:IsFuelLow()then -combatready=false -end -if flightgroup:IsHolding()or flightgroup:IsLanding()or flightgroup:IsLanded()or flightgroup:IsArrived()or flightgroup:IsDead()or flightgroup:IsStopped()then -combatready=false -end -if combatready then -self:I(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY") -table.insert(assets,asset) -end -end -else -if Npayloads>0 and self:IsRepaired(asset)and(not asset.requested)then -table.insert(assets,asset) -Npayloads=Npayloads-1 -end -end -end -end -return assets -end -function SQUADRON:GetRepairTime(Asset) -if Asset.Treturned then -local t=self.maintenancetime -t=t+Asset.damage*self.repairtime -local dt=timer.getAbsTime()-Asset.Treturned -local T=t-dt -return T -else -return 0 -end -end -function SQUADRON:IsRepaired(Asset) -if Asset.Treturned then -local Tnow=timer.getAbsTime() -local Trepaired=Asset.Treturned+self.maintenancetime -if Tnow>=Trepaired then -return true -else -return false -end -else -return true -end -end -function SQUADRON:CheckMissionType(MissionType,PossibleTypes) -if type(PossibleTypes)=="string"then -PossibleTypes={PossibleTypes} -end -for _,canmission in pairs(PossibleTypes)do -if canmission==MissionType then -return true -end -end -return false -end -function SQUADRON:CheckMissionCapability(MissionType,Capabilities) -for _,cap in pairs(Capabilities)do -local capability=cap -if capability.MissionType==MissionType then -return true -end -end -return false -end -AIRWING={ -ClassName="AIRWING", -verbose=0, -lid=nil, -menu=nil, -squadrons={}, -missionqueue={}, -payloads={}, -payloadcounter=0, -pointsCAP={}, -pointsTANKER={}, -pointsAWACS={}, -wingcommander=nil, -markpoints=false, -} -AIRWING.version="0.5.1" -function AIRWING:New(warehousename,airwingname) -local self=BASE:Inherit(self,WAREHOUSE:New(warehousename,airwingname)) -if not self then -BASE:E(string.format("ERROR: Could not find warehouse %s!",warehousename)) -return nil -end -self.lid=string.format("AIRWING %s | ",self.alias) -self:AddTransition("*","MissionRequest","*") -self:AddTransition("*","MissionCancel","*") -self:AddTransition("*","SquadAssetReturned","*") -self:AddTransition("*","FlightOnMission","*") -self.nflightsCAP=0 -self.nflightsAWACS=0 -self.nflightsTANKERboom=0 -self.nflightsTANKERprobe=0 -self.nflightsRecoveryTanker=0 -self.nflightsRescueHelo=0 -self.markpoints=false -return self -end -function AIRWING:AddSquadron(Squadron) -table.insert(self.squadrons,Squadron) -self:AddAssetToSquadron(Squadron,Squadron.Ngroups) -if Squadron.attribute==GROUP.Attribute.AIR_AWACS then -self:NewPayload(Squadron.templategroup,-1,AUFTRAG.Type.AWACS) -elseif Squadron.attribute==GROUP.Attribute.AIR_TANKER then -self:NewPayload(Squadron.templategroup,-1,AUFTRAG.Type.TANKER) -end -Squadron:SetAirwing(self) -if Squadron:IsStopped()then -Squadron:Start() -end -return self -end -function AIRWING:NewPayload(Unit,Npayloads,MissionTypes,Performance) -Performance=Performance or 50 -if type(Unit)=="string"then -local name=Unit -Unit=UNIT:FindByName(name) -if not Unit then -Unit=GROUP:FindByName(name) -end -end -if Unit then -if Unit:IsInstanceOf("GROUP")then -Unit=Unit:GetUnit(1) -end -if MissionTypes and type(MissionTypes)~="table"then -MissionTypes={MissionTypes} -end -local payload={} -payload.uid=self.payloadcounter -payload.unitname=Unit:GetName() -payload.aircrafttype=Unit:GetTypeName() -payload.pylons=Unit:GetTemplatePayload() -payload.unlimited=Npayloads<0 -if payload.unlimited then -payload.navail=1 -else -payload.navail=Npayloads or 99 -end -payload.capabilities={} -for _,missiontype in pairs(MissionTypes)do -local capability={} -capability.MissionType=missiontype -capability.Performance=Performance -table.insert(payload.capabilities,capability) -end -if not self:CheckMissionType(AUFTRAG.Type.ORBIT,MissionTypes)then -local capability={} -capability.MissionType=AUFTRAG.Type.ORBIT -capability.Performance=50 -table.insert(payload.capabilities,capability) -end -self:T(self.lid..string.format("Adding new payload from unit %s for aircraft type %s: ID=%d, N=%d (unlimited=%s), performance=%d, missions: %s", -payload.unitname,payload.aircrafttype,payload.uid,payload.navail,tostring(payload.unlimited),Performance,table.concat(MissionTypes,", "))) -table.insert(self.payloads,payload) -self.payloadcounter=self.payloadcounter+1 -return payload -end -self:E(self.lid.."ERROR: No UNIT found to create PAYLOAD!") -return nil -end -function AIRWING:AddPayloadCapability(Payload,MissionTypes,Performance) -if MissionTypes and type(MissionTypes)~="table"then -MissionTypes={MissionTypes} -end -Payload.capabilities=Payload.capabilities or{} -for _,missiontype in pairs(MissionTypes)do -local capability={} -capability.MissionType=missiontype -capability.Performance=Performance -table.insert(Payload.capabilities,capability) -end -return self -end -function AIRWING:FetchPayloadFromStock(UnitType,MissionType,Payloads) -if not self.payloads or#self.payloads==0 then -self:T(self.lid.."WARNING: No payloads in stock!") -return nil -end -if self.verbose>=4 then -self:I(self.lid..string.format("Looking for payload for unit type=%s and mission type=%s",UnitType,MissionType)) -for i,_payload in pairs(self.payloads)do -local payload=_payload -local performance=self:GetPayloadPeformance(payload,MissionType) -self:I(self.lid..string.format("[%d] Payload type=%s navail=%d unlimited=%s",i,payload.aircrafttype,payload.navail,tostring(payload.unlimited))) -end -end -local function sortpayloads(a,b) -local pA=a -local pB=b -if a and b then -local performanceA=self:GetPayloadPeformance(a,MissionType) -local performanceB=self:GetPayloadPeformance(b,MissionType) -return(performanceA>performanceB)or(performanceA==performanceB and a.unlimited==true)or(performanceA==performanceB and a.unlimited==true and b.unlimited==true and a.navail>b.navail) -elseif not a then -self:I(self.lid..string.format("FF ERROR in sortpayloads: a is nil")) -return false -elseif not b then -self:I(self.lid..string.format("FF ERROR in sortpayloads: b is nil")) -return true -else -self:I(self.lid..string.format("FF ERROR in sortpayloads: a and b are nil")) -return false -end -end -local function _checkPayloads(payload) -if Payloads then -for _,Payload in pairs(Payloads)do -if Payload.uid==payload.uid then -return true -end -end -else -return nil -end -return false -end -local payloads={} -for _,_payload in pairs(self.payloads)do -local payload=_payload -local specialpayload=_checkPayloads(payload) -local compatible=self:CheckMissionCapability(MissionType,payload.capabilities) -local goforit=specialpayload or(specialpayload==nil and compatible) -if payload.aircrafttype==UnitType and payload.navail>0 and goforit then -table.insert(payloads,payload) -end -end -if self.verbose>=4 then -self:I(self.lid..string.format("Sorted payloads for mission type X and aircraft type=Y:")) -for _,_payload in ipairs(self.payloads)do -local payload=_payload -if payload.aircrafttype==UnitType and self:CheckMissionCapability(MissionType,payload.capabilities)then -local performace=self:GetPayloadPeformance(payload,MissionType) -self:I(self.lid..string.format("FF %s payload for %s: avail=%d performace=%d",MissionType,payload.aircrafttype,payload.navail,performace)) -end -end -end -if#payloads==0 then -self:T(self.lid.."Warning could not find a payload for airframe X mission type Y!") -return nil -elseif#payloads==1 then -local payload=payloads[1] -if not payload.unlimited then -payload.navail=payload.navail-1 -end -return payload -else -table.sort(payloads,sortpayloads) -local payload=payloads[1] -if not payload.unlimited then -payload.navail=payload.navail-1 -end -return payload -end -end -function AIRWING:ReturnPayloadFromAsset(asset) -local payload=asset.payload -if payload then -if not payload.unlimited then -payload.navail=payload.navail+1 -end -asset.payload=nil -else -self:E(self.lid.."ERROR: asset had no payload attached!") -end -end -function AIRWING:AddAssetToSquadron(Squadron,Nassets) -if Squadron then -local Group=GROUP:FindByName(Squadron.templatename) -if Group then -local text=string.format("Adding asset %s to squadron %s",Group:GetName(),Squadron.name) -self:T(self.lid..text) -self:AddAsset(Group,Nassets,nil,nil,nil,nil,Squadron.skill,Squadron.livery,Squadron.name) -else -self:E(self.lid.."ERROR: Group does not exist!") -end -else -self:E(self.lid.."ERROR: Squadron does not exit!") -end -return self -end -function AIRWING:GetSquadron(SquadronName) -for _,_squadron in pairs(self.squadrons)do -local squadron=_squadron -if squadron.name==SquadronName then -return squadron -end -end -return nil -end -function AIRWING:SetVerbosity(VerbosityLevel) -self.verbose=VerbosityLevel or 0 -return self -end -function AIRWING:GetSquadronOfAsset(Asset) -return self:GetSquadron(Asset.squadname) -end -function AIRWING:RemoveAssetFromSquadron(Asset) -local squad=self:GetSquadronOfAsset(Asset) -if squad then -squad:DelAsset(Asset) -end -end -function AIRWING:AddMission(Mission) -Mission:Queued(self) -table.insert(self.missionqueue,Mission) -local text=string.format("Added mission %s (type=%s). Starting at %s. Stopping at %s", -tostring(Mission.name),tostring(Mission.type),UTILS.SecondsToClock(Mission.Tstart,true),Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop,true)or"INF") -self:T(self.lid..text) -return self -end -function AIRWING:RemoveMission(Mission) -for i,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission.auftragsnummer==Mission.auftragsnummer then -table.remove(self.missionqueue,i) -break -end -end -return self -end -function AIRWING:SetNumberCAP(n) -self.nflightsCAP=n or 1 -return self -end -function AIRWING:SetNumberTankerBoom(Nboom) -self.nflightsTANKERboom=Nboom or 1 -return self -end -function AIRWING:ShowPatrolPointMarkers(onoff) -if onoff then -self.markpoints=true -else -self.markpoints=false -end -return self -end -function AIRWING:SetNumberTankerProbe(Nprobe) -self.nflightsTANKERprobe=Nprobe or 1 -return self -end -function AIRWING:SetNumberAWACS(n) -self.nflightsAWACS=n or 1 -return self -end -function AIRWING:SetNumberRescuehelo(n) -self.nflightsRescueHelo=n or 1 -return self -end -function AIRWING:_PatrolPointMarkerText(point) -local text=string.format("%s Occupied=%d, \nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts", -point.type,point.noccupied,point.heading,point.leg,point.altitude,point.speed) -return text -end -function AIRWING:UpdatePatrolPointMarker(point) -if self.markpoints then -local text=string.format("%s Occupied=%d\nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts", -point.type,point.noccupied,point.heading,point.leg,point.altitude,point.speed) -point.marker:UpdateText(text,1) -end -end -function AIRWING:NewPatrolPoint(Type,Coordinate,Altitude,Speed,Heading,LegLength) -local patrolpoint={} -patrolpoint.type=Type or"Unknown" -patrolpoint.coord=Coordinate or self:GetCoordinate():Translate(UTILS.NMToMeters(math.random(10,15)),math.random(360)) -patrolpoint.heading=Heading or math.random(360) -patrolpoint.leg=LegLength or 15 -patrolpoint.altitude=Altitude or math.random(10,20)*1000 -patrolpoint.speed=Speed or 350 -patrolpoint.noccupied=0 -if self.markpoints then -patrolpoint.marker=MARKER:New(Coordinate,"New Patrol Point"):ToAll() -AIRWING.UpdatePatrolPointMarker(patrolpoint) -end -return patrolpoint -end -function AIRWING:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength) -local patrolpoint=self:NewPatrolPoint("CAP",Coordinate,Altitude,Speed,Heading,LegLength) -table.insert(self.pointsCAP,patrolpoint) -return self -end -function AIRWING:AddPatrolPointTANKER(Coordinate,Altitude,Speed,Heading,LegLength) -local patrolpoint=self:NewPatrolPoint("Tanker",Coordinate,Altitude,Speed,Heading,LegLength) -table.insert(self.pointsTANKER,patrolpoint) -return self -end -function AIRWING:AddPatrolPointAWACS(Coordinate,Altitude,Speed,Heading,LegLength) -local patrolpoint=self:NewPatrolPoint("AWACS",Coordinate,Altitude,Speed,Heading,LegLength) -table.insert(self.pointsAWACS,patrolpoint) -return self -end -function AIRWING:onafterStart(From,Event,To) -self:GetParent(self).onafterStart(self,From,Event,To) -self:I(self.lid..string.format("Starting AIRWING v%s",AIRWING.version)) -end -function AIRWING:onafterStatus(From,Event,To) -self:GetParent(self).onafterStatus(self,From,Event,To) -local fsmstate=self:GetState() -self:CheckCAP() -self:CheckTANKER() -self:CheckAWACS() -self:CheckRescuhelo() -if self.verbose>=1 then -local Nmissions=self:CountMissionsInQueue() -local Npayloads=self:CountPayloadsInStock(AUFTRAG.Type) -local Npq,Np,Nq=self:CountAssetsOnMission() -local assets=string.format("%d (OnMission: Total=%d, Active=%d, Queued=%d)",self:CountAssets(),Npq,Np,Nq) -local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d, Assets=%s",fsmstate,Nmissions,Npayloads,#self.payloads,#self.squadrons,assets) -self:I(self.lid..text) -end -if self.verbose>=2 then -local text=string.format("Missions Total=%d:",#self.missionqueue) -for i,_mission in pairs(self.missionqueue)do -local mission=_mission -local prio=string.format("%d/%s",mission.prio,tostring(mission.importance));if mission.urgent then prio=prio.." (!)"end -local assets=string.format("%d/%d",mission:CountOpsGroups(),mission.nassets) -local target=string.format("%d/%d Damage=%.1f",mission:CountMissionTargets(),mission:GetTargetInitialNumber(),mission:GetTargetDamage()) -text=text..string.format("\n[%d] %s %s: Status=%s, Prio=%s, Assets=%s, Targets=%s",i,mission.name,mission.type,mission.status,prio,assets,target) -end -self:I(self.lid..text) -end -if self.verbose>=3 then -local text="Squadrons:" -for i,_squadron in pairs(self.squadrons)do -local squadron=_squadron -local callsign=squadron.callsignName and UTILS.GetCallsignName(squadron.callsignName)or"N/A" -local modex=squadron.modex and squadron.modex or-1 -local skill=squadron.skill and tostring(squadron.skill)or"N/A" -text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s",squadron.name,squadron:GetState(),squadron.aircrafttype,squadron:CountAssetsInStock(),#squadron.assets,callsign,modex,skill) -end -self:I(self.lid..text) -end -self:_CheckMissions() -local mission=self:_GetNextMission() -if mission then -self:MissionRequest(mission) -end -end -function AIRWING:_GetPatrolData(PatrolPoints) -local function sort(a,b) -return a.noccupied0 then -table.sort(PatrolPoints,sort) -return PatrolPoints[1] -else -return self:NewPatrolPoint() -end -end -function AIRWING:CheckCAP() -local Ncap=self:CountMissionsInQueue({AUFTRAG.Type.GCICAP,AUFTRAG.Type.INTERCEPT}) -for i=1,self.nflightsCAP-Ncap do -local patrol=self:_GetPatrolData(self.pointsCAP) -local altitude=patrol.altitude+1000*patrol.noccupied -local missionCAP=AUFTRAG:NewGCICAP(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg) -missionCAP.patroldata=patrol -patrol.noccupied=patrol.noccupied+1 -if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end -self:AddMission(missionCAP) -end -return self -end -function AIRWING:CheckTANKER() -local Nboom=0 -local Nprob=0 -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission:IsNotOver()and mission.type==AUFTRAG.Type.TANKER then -if mission.refuelSystem==0 then -Nboom=Nboom+1 -elseif mission.refuelSystem==1 then -Nprob=Nprob+1 -end -end -end -for i=1,self.nflightsTANKERboom-Nboom do -local patrol=self:_GetPatrolData(self.pointsTANKER) -local altitude=patrol.altitude+1000*patrol.noccupied -local mission=AUFTRAG:NewTANKER(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg,1) -mission.patroldata=patrol -patrol.noccupied=patrol.noccupied+1 -if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end -self:AddMission(mission) -end -for i=1,self.nflightsTANKERprobe-Nprob do -local patrol=self:_GetPatrolData(self.pointsTANKER) -local altitude=patrol.altitude+1000*patrol.noccupied -local mission=AUFTRAG:NewTANKER(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg,0) -mission.patroldata=patrol -patrol.noccupied=patrol.noccupied+1 -if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end -self:AddMission(mission) -end -return self -end -function AIRWING:CheckAWACS() -local N=self:CountMissionsInQueue({AUFTRAG.Type.AWACS}) -for i=1,self.nflightsAWACS-N do -local patrol=self:_GetPatrolData(self.pointsAWACS) -local altitude=patrol.altitude+1000*patrol.noccupied -local mission=AUFTRAG:NewAWACS(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg) -mission.patroldata=patrol -patrol.noccupied=patrol.noccupied+1 -if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end -self:AddMission(mission) -end -return self -end -function AIRWING:CheckRescuhelo() -local N=self:CountMissionsInQueue({AUFTRAG.Type.RESCUEHELO}) -local name=self.airbase:GetName() -local carrier=UNIT:FindByName(name) -for i=1,self.nflightsRescueHelo-N do -local mission=AUFTRAG:NewRESCUEHELO(carrier) -self:AddMission(mission) -end -return self -end -function AIRWING:GetTankerForFlight(flightgroup) -local tankers=self:GetAssetsOnMission(AUFTRAG.Type.TANKER) -if#tankers>0 then -local tankeropt={} -for _,_tanker in pairs(tankers)do -local tanker=_tanker -if flightgroup.refueltype and flightgroup.refueltype==tanker.flightgroup.tankertype then -local tankercoord=tanker.flightgroup.group:GetCoordinate() -local assetcoord=flightgroup.group:GetCoordinate() -local dist=assetcoord:Get2DDistance(tankercoord) -if dist>5 then -table.insert(tankeropt,{tanker=tanker,dist=dist}) -end -end -end -table.sort(tankeropt,function(a,b)return a.dist0 then -return tankeropt[1].tanker -else -return nil -end -end -return nil -end -function AIRWING:_CheckMissions() -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission:IsNotOver()and mission:IsReadyToCancel()then -mission:Cancel() -end -end -end -function AIRWING:_GetNextMission() -local Nmissions=#self.missionqueue -if Nmissions==0 then -return nil -end -local function _sort(a,b) -local taskA=a -local taskB=b -return(taskA.prio0 then -self:E(self.lid..string.format("ERROR: mission %s of type %s has already assets attached!",mission.name,mission.type)) -end -mission.assets={} -for i=1,mission.nassets do -local asset=assets[i] -if not asset.payload then -self:E(self.lid.."ERROR: No payload for asset! This should not happen!") -end -mission:AddAsset(asset) -end -for i=mission.nassets+1,#assets do -local asset=assets[i] -for _,uid in pairs(gotpayload)do -if uid==asset.uid then -self:ReturnPayloadFromAsset(asset) -break -end -end -end -return mission -end -end -end -return nil -end -function AIRWING:CalculateAssetMissionScore(asset,Mission,includePayload) -local score=0 -if asset.skill==AI.Skill.AVERAGE then -score=score+0 -elseif asset.skill==AI.Skill.GOOD then -score=score+10 -elseif asset.skill==AI.Skill.HIGH then -score=score+20 -elseif asset.skill==AI.Skill.EXCELLENT then -score=score+30 -end -local squad=self:GetSquadronOfAsset(asset) -local missionperformance=squad:GetMissionPeformance(Mission.type) -score=score+missionperformance -if includePayload and asset.payload then -score=score+self:GetPayloadPeformance(asset.payload,Mission.type) -end -if Mission.type==AUFTRAG.Type.INTERCEPT then -if asset.spawned then -self:T(self.lid.."Adding 25 to asset because it is spawned") -score=score+25 -end -end -return score -end -function AIRWING:_OptimizeAssetSelection(assets,Mission,includePayload) -local TargetVec2=Mission:GetTargetVec2() -local dStock=UTILS.VecDist2D(TargetVec2,self:GetVec2()) -local distmin=math.huge -local distmax=0 -for _,_asset in pairs(assets)do -local asset=_asset -if asset.spawned then -local group=GROUP:FindByName(asset.spawngroupname) -asset.dist=UTILS.VecDist2D(group:GetVec2(),TargetVec2) -else -asset.dist=dStock -end -if asset.distdistmax then -distmax=asset.dist -end -end -for _,_asset in pairs(assets)do -local asset=_asset -asset.score=self:CalculateAssetMissionScore(asset,Mission,includePayload) -end -local function optimize(a,b) -local assetA=a -local assetB=b -return(assetA.score>assetB.score)or(assetA.score==assetB.score and assetA.dist0 then -for i,_asset in pairs(Assetlist)do -local asset=_asset -asset.requested=true -if Mission.missionTask then -asset.missionTask=Mission.missionTask -end -end -self:AddRequest(self,WAREHOUSE.Descriptor.ASSETLIST,Assetlist,#Assetlist,nil,nil,Mission.prio,tostring(Mission.auftragsnummer)) -Mission.requestID=self.queueid -end -end -function AIRWING:onafterMissionCancel(From,Event,To,Mission) -self:I(self.lid..string.format("Cancel mission %s",Mission.name)) -local Ngroups=Mission:CountOpsGroups() -if Mission:IsPlanned()or Mission:IsQueued()or Mission:IsRequested()or Ngroups==0 then -Mission:Done() -else -for _,_asset in pairs(Mission.assets)do -local asset=_asset -local flightgroup=asset.flightgroup -if flightgroup then -flightgroup:MissionCancel(Mission) -end -asset.requested=nil -end -end -if Mission.requestID then -self:_DeleteQueueItemByID(Mission.requestID,self.queue) -end -end -function AIRWING:onafterNewAsset(From,Event,To,asset,assignment) -self:GetParent(self).onafterNewAsset(self,From,Event,To,asset,assignment) -local text=string.format("New asset %s with assignment %s and request assignment %s",asset.spawngroupname,tostring(asset.assignment),tostring(assignment)) -self:T3(self.lid..text) -local squad=self:GetSquadron(asset.assignment) -if squad then -if asset.assignment==assignment then -local nunits=#asset.template.units -local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d %s",squad.name,assignment,asset.unittype,asset.attribute,nunits,tostring(squad.ngrouping)) -self:T(self.lid..text) -if squad.ngrouping then -local template=asset.template -local N=math.max(#template.units,squad.ngrouping) -for i=1,N do -local unit=template.units[i] -if i>nunits then -table.insert(template.units,UTILS.DeepCopy(template.units[1])) -end -if squad.ngroupingnunits then -unit=nil -end -end -asset.nunits=squad.ngrouping -end -squad:GetCallsign(asset) -squad:GetModex(asset) -asset.spawngroupname=string.format("%s_AID-%d",squad.name,asset.uid) -squad:AddAsset(asset) -else -self:SquadAssetReturned(squad,asset) -end -end -end -function AIRWING:onafterSquadAssetReturned(From,Event,To,Squadron,Asset) -self:T(self.lid..string.format("Asset %s from squadron %s returned! asset.assignment=\"%s\"",Asset.spawngroupname,Squadron.name,tostring(Asset.assignment))) -if Asset.flightgroup and not Asset.flightgroup:IsStopped()then -Asset.flightgroup:Stop() -end -self:ReturnPayloadFromAsset(Asset) -if Asset.tacan then -Squadron:ReturnTacan(Asset.tacan) -end -Asset.Treturned=timer.getAbsTime() -end -function AIRWING:onafterAssetSpawned(From,Event,To,group,asset,request) -self:GetParent(self).onafterAssetSpawned(self,From,Event,To,group,asset,request) -local flightgroup=self:_CreateFlightGroup(asset) -asset.flightgroup=flightgroup -asset.requested=nil -asset.Treturned=nil -local squadron=self:GetSquadronOfAsset(asset) -local Tacan=squadron:FetchTacan() -if Tacan then -asset.tacan=Tacan -end -local radioFreq,radioModu=squadron:GetRadio() -if radioFreq then -flightgroup:SwitchRadio(radioFreq,radioModu) -end -if squadron.fuellow then -flightgroup:SetFuelLowThreshold(squadron.fuellow) -end -if squadron.fuellowRefuel then -flightgroup:SetFuelLowRefuel(squadron.fuellowRefuel) -end -local mission=self:GetMissionByID(request.assignment) -if mission then -if Tacan then -mission:SetTACAN(Tacan,Morse,UnitName,Band) -end -asset.flightgroup:AddMission(mission) -self:FlightOnMission(flightgroup,mission) -else -if Tacan then -flightgroup:SwitchTACAN(Tacan,Morse,UnitName,Band) -end -end -if self.wingcommander and self.wingcommander.chief then -self.wingcommander.chief.detectionset:AddGroup(asset.flightgroup.group) -end -end -function AIRWING:onafterAssetDead(From,Event,To,asset,request) -self:GetParent(self).onafterAssetDead(self,From,Event,To,asset,request) -if self.wingcommander and self.wingcommander.chief then -self.wingcommander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname}) -end -end -function AIRWING:onafterDestroyed(From,Event,To) -self:I(self.lid.."Airwing warehouse destroyed!") -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -mission:Cancel() -end -for _,_squadron in pairs(self.squadrons)do -local squadron=_squadron -squadron:Stop() -end -self:GetParent(self).onafterDestroyed(self,From,Event,To) -end -function AIRWING:onafterRequest(From,Event,To,Request) -local assets=Request.cargoassets -local Mission=self:GetMissionByID(Request.assignment) -if Mission and assets then -for _,_asset in pairs(assets)do -local asset=_asset -end -end -self:GetParent(self).onafterRequest(self,From,Event,To,Request) -end -function AIRWING:onafterSelfRequest(From,Event,To,groupset,request) -self:GetParent(self).onafterSelfRequest(self,From,Event,To,groupset,request) -local mission=self:GetMissionByID(request.assignment) -for _,_asset in pairs(request.assets)do -local asset=_asset -end -for _,_group in pairs(groupset:GetSet())do -local group=_group -end -end -function AIRWING:_CreateFlightGroup(asset) -local flightgroup=FLIGHTGROUP:New(asset.spawngroupname) -flightgroup:SetAirwing(self) -flightgroup.squadron=self:GetSquadronOfAsset(asset) -flightgroup.homebase=self.airbase -return flightgroup -end -function AIRWING:IsAssetOnMission(asset,MissionTypes) -if MissionTypes then -if type(MissionTypes)~="table"then -MissionTypes={MissionTypes} -end -else -MissionTypes=AUFTRAG.Type -end -if asset.flightgroup and asset.flightgroup:IsAlive()then -for _,_mission in pairs(asset.flightgroup.missionqueue or{})do -local mission=_mission -if mission:IsNotOver()then -local status=mission:GetGroupStatus(asset.flightgroup) -if(status==AUFTRAG.GroupStatus.STARTED or status==AUFTRAG.GroupStatus.EXECUTING)and self:CheckMissionType(mission.type,MissionTypes)then -return true -end -end -end -end -return false -end -function AIRWING:GetAssetCurrentMission(asset) -if asset.flightgroup then -return asset.flightgroup:GetMissionCurrent() -end -return nil -end -function AIRWING:CountPayloadsInStock(MissionTypes,UnitTypes,Payloads) -if MissionTypes then -if type(MissionTypes)=="string"then -MissionTypes={MissionTypes} -end -end -if UnitTypes then -if type(UnitTypes)=="string"then -UnitTypes={UnitTypes} -end -end -local function _checkUnitTypes(payload) -if UnitTypes then -for _,unittype in pairs(UnitTypes)do -if unittype==payload.aircrafttype then -return true -end -end -else -return true -end -return false -end -local function _checkPayloads(payload) -if Payloads then -for _,Payload in pairs(Payloads)do -if Payload.uid==payload.uid then -return true -end -end -else -return nil -end -return false -end -local n=0 -for _,_payload in pairs(self.payloads)do -local payload=_payload -for _,MissionType in pairs(MissionTypes)do -local specialpayload=_checkPayloads(payload) -local compatible=self:CheckMissionCapability(MissionType,payload.capabilities) -local goforit=specialpayload or(specialpayload==nil and compatible) -if goforit and _checkUnitTypes(payload)then -if payload.unlimited then -return 999 -else -n=n+payload.navail -end -end -end -end -return n -end -function AIRWING:CountMissionsInQueue(MissionTypes) -MissionTypes=MissionTypes or AUFTRAG.Type -local N=0 -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission:IsNotOver()and self:CheckMissionType(mission.type,MissionTypes)then -N=N+1 -end -end -return N -end -function AIRWING:CountAssets() -local N=0 -for _,_squad in pairs(self.squadrons)do -local squad=_squad -N=N+#squad.assets -end -return N -end -function AIRWING:CountAssetsOnMission(MissionTypes,Squadron) -local Nq=0 -local Np=0 -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if self:CheckMissionType(mission.type,MissionTypes or AUFTRAG.Type)then -for _,_asset in pairs(mission.assets or{})do -local asset=_asset -if Squadron==nil or Squadron.name==asset.squadname then -local request,isqueued=self:GetRequestByID(mission.requestID) -if isqueued then -Nq=Nq+1 -else -Np=Np+1 -end -end -end -end -end -return Np+Nq,Np,Nq -end -function AIRWING:GetAssetsOnMission(MissionTypes) -local assets={} -local Np=0 -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if self:CheckMissionType(mission.type,MissionTypes)then -for _,_asset in pairs(mission.assets or{})do -local asset=_asset -table.insert(assets,asset) -end -end -end -return assets -end -function AIRWING:GetAircraftTypes(onlyactive,squadrons) -local unittypes={} -for _,_squadron in pairs(squadrons or self.squadrons)do -local squadron=_squadron -if(not onlyactive)or squadron:IsOnDuty()then -local gotit=false -for _,unittype in pairs(unittypes)do -if squadron.aircrafttype==unittype then -gotit=true -break -end -end -if not gotit then -table.insert(unittypes,squadron.aircrafttype) -end -end -end -return unittypes -end -function AIRWING:CanMission(Mission) -local Can=true -local Assets={} -local squadrons=Mission.squadrons or self.squadrons -local unittypes=self:GetAircraftTypes(true,squadrons) -local Npayloads=self:CountPayloadsInStock(Mission.type,unittypes,Mission.payloads) -if Npayloads#Assets then -self:T(self.lid..string.format("INFO: Not enough assets available! Got %d but need at least %d",#Assets,Mission.nassets)) -Can=false -end -return Can,Assets -end -function AIRWING:RecruitAssets(Mission) -end -function AIRWING:CheckMissionType(MissionType,PossibleTypes) -if type(PossibleTypes)=="string"then -PossibleTypes={PossibleTypes} -end -for _,canmission in pairs(PossibleTypes)do -if canmission==MissionType then -return true -end -end -return false -end -function AIRWING:CheckMissionCapability(MissionType,Capabilities) -for _,cap in pairs(Capabilities)do -local capability=cap -if capability.MissionType==MissionType then -return true -end -end -return false -end -function AIRWING:GetPayloadPeformance(Payload,MissionType) -if Payload then -for _,Capability in pairs(Payload.capabilities)do -local capability=Capability -if capability.MissionType==MissionType then -return capability.Performance -end -end -else -self:E(self.lid.."ERROR: Payload is nil!") -end -return-1 -end -function AIRWING:GetPayloadMissionTypes(Payload) -local missiontypes={} -for _,Capability in pairs(Payload.capabilities)do -local capability=Capability -table.insert(missiontypes,capability.MissionType) -end -return missiontypes -end -function AIRWING:GetMissionByID(mid) -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission.auftragsnummer==tonumber(mid)then -return mission -end -end -return nil -end -function AIRWING:GetMissionFromRequestID(RequestID) -for _,_mission in pairs(self.missionqueue)do -local mission=_mission -if mission.requestID and mission.requestID==RequestID then -return mission -end -end -return nil -end -function AIRWING:GetMissionFromRequest(Request) -return self:GetMissionFromRequestID(Request.uid) -end -INTEL={ -ClassName="INTEL", -verbose=0, -lid=nil, -alias=nil, -filterCategory={}, -detectionset=nil, -Contacts={}, -ContactsLost={}, -ContactsUnknown={}, -Clusters={}, -clustercounter=1, -clusterradius=15, -} -INTEL.version="0.2.1" -function INTEL:New(DetectionSet,Coalition,Alias) -local self=BASE:Inherit(self,FSM:New()) -self.detectionset=DetectionSet or SET_GROUP:New() -if Coalition and type(Coalition)=="string"then -if Coalition=="blue"then -Coalition=coalition.side.BLUE -elseif Coalition=="red"then -Coalition=coalition.side.RED -elseif Coalition=="neutral"then -Coalition=coalition.side.NEUTRAL -else -self:E("ERROR: Unknown coalition in INTEL!") -end -end -self.coalition=Coalition or DetectionSet:CountAlive()>0 and DetectionSet:GetFirst():GetCoalition()or nil -if self.coalition then -local coalitionname=UTILS.GetCoalitionName(self.coalition):lower() -self.detectionset:FilterCoalitions(coalitionname) -end -self.detectionset:FilterOnce() -if Alias then -self.alias=tostring(Alias) -else -self.alias="SPECTRE" -if self.coalition then -if self.coalition==coalition.side.RED then -self.alias="KGB" -elseif self.coalition==coalition.side.BLUE then -self.alias="CIA" -end -end -end -self.lid=string.format("INTEL %s (%s) | ",self.alias,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown") -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Running") -self:AddTransition("*","Status","*") -self:AddTransition("*","Detect","*") -self:AddTransition("*","NewContact","*") -self:AddTransition("*","LostContact","*") -self:AddTransition("*","NewCluster","*") -self:AddTransition("*","LostCluster","*") -self:SetForgetTime() -self:SetAcceptZones() -self:SetRejectZones() -return self -end -function INTEL:SetAcceptZones(AcceptZoneSet) -self.acceptzoneset=AcceptZoneSet or SET_ZONE:New() -return self -end -function INTEL:AddAcceptZone(AcceptZone) -self.acceptzoneset:AddZone(AcceptZone) -return self -end -function INTEL:RemoveAcceptZone(AcceptZone) -self.acceptzoneset:Remove(AcceptZone:GetName(),true) -return self -end -function INTEL:SetRejectZones(RejectZoneSet) -self.rejectzoneset=RejectZoneSet or SET_ZONE:New() -return self -end -function INTEL:AddRejectZone(RejectZone) -self.rejectzoneset:AddZone(RejectZone) -return self -end -function INTEL:RemoveRejectZone(RejectZone) -self.rejectzoneset:Remove(RejectZone:GetName(),true) -return self -end -function INTEL:SetForgetTime(TimeInterval) -self.dTforget=TimeInterval or 120 -return self -end -function INTEL:SetFilterCategory(Categories) -if type(Categories)~="table"then -Categories={Categories} -end -self.filterCategory=Categories -local text="Filter categories: " -for _,category in pairs(self.filterCategory)do -text=text..string.format("%d,",category) -end -self:T(self.lid..text) -return self -end -function INTEL:FilterCategoryGroup(GroupCategories) -if type(GroupCategories)~="table"then -GroupCategories={GroupCategories} -end -self.filterCategoryGroup=GroupCategories -local text="Filter group categories: " -for _,category in pairs(self.filterCategoryGroup)do -text=text..string.format("%d,",category) -end -self:T(self.lid..text) -return self -end -function INTEL:SetClusterAnalysis(Switch,Markers) -self.clusteranalysis=Switch -self.clustermarkers=Markers -return self -end -function INTEL:SetVerbosity(Verbosity) -self.verbose=Verbosity or 2 -return self -end -function INTEL:AddMissionToContact(Contact,Mission) -if Mission and Contact then -Contact.mission=Mission -end -return self -end -function INTEL:AddMissionToCluster(Cluster,Mission) -if Mission and Cluster then -Cluster.mission=Mission -end -return self -end -function INTEL:SetClusterRadius(radius) -local radius=radius or 15 -self.clusterradius=radius -return self -end -function INTEL:onafterStart(From,Event,To) -local text=string.format("Starting INTEL v%s",self.version) -self:I(self.lid..text) -self:__Status(-math.random(10)) -end -function INTEL:onafterStatus(From,Event,To) -local fsmstate=self:GetState() -self.ContactsLost={} -self.ContactsUnknown={} -self:UpdateIntel() -local Ncontacts=#self.Contacts -local Nclusters=#self.Clusters -if self.verbose>=1 then -local text=string.format("Status %s [Agents=%s]: Contacts=%d, Clusters=%d, New=%d, Lost=%d",fsmstate,self.detectionset:CountAlive(),Ncontacts,Nclusters,#self.ContactsUnknown,#self.ContactsLost) -self:I(self.lid..text) -end -if self.verbose>=2 and Ncontacts>0 then -local text="Detected Contacts:" -for _,_contact in pairs(self.Contacts)do -local contact=_contact -local dT=timer.getAbsTime()-contact.Tdetected -text=text..string.format("\n- %s (%s): %s, units=%d, T=%d sec",contact.categoryname,contact.attribute,contact.groupname,contact.group:CountAliveUnits(),dT) -if contact.mission then -local mission=contact.mission -text=text..string.format(" mission name=%s type=%s target=%s",mission.name,mission.type,mission:GetTargetName()or"unkown") -end -end -self:I(self.lid..text) -end -self:__Status(-60) -end -function INTEL:UpdateIntel() -local DetectedUnits={} -local RecceDetecting={} -for _,_group in pairs(self.detectionset.Set or{})do -local group=_group -if group and group:IsAlive()then -for _,_recce in pairs(group:GetUnits())do -local recce=_recce -self:GetDetectedUnits(recce,DetectedUnits,RecceDetecting) -end -end -end -local remove={} -for unitname,_unit in pairs(DetectedUnits)do -local unit=_unit -if self.acceptzoneset:Count()>0 then -local inzone=false -for _,_zone in pairs(self.acceptzoneset.Set)do -local zone=_zone -if unit:IsInZone(zone)then -inzone=true -break -end -end -if not inzone then -table.insert(remove,unitname) -end -end -if self.rejectzoneset:Count()>0 then -local inzone=false -for _,_zone in pairs(self.rejectzoneset.Set)do -local zone=_zone -if unit:IsInZone(zone)then -inzone=true -break -end -end -if inzone then -table.insert(remove,unitname) -end -end -if#self.filterCategory>0 then -local unitcategory=unit:GetUnitCategory() -local keepit=false -for _,filtercategory in pairs(self.filterCategory)do -if unitcategory==filtercategory then -keepit=true -break -end -end -if not keepit then -self:T(self.lid..string.format("Removing unit %s category=%d",unitname,unit:GetCategory())) -table.insert(remove,unitname) -end -end -end -for _,unitname in pairs(remove)do -DetectedUnits[unitname]=nil -end -local DetectedGroups={} -local RecceGroups={} -for unitname,_unit in pairs(DetectedUnits)do -local unit=_unit -local group=unit:GetGroup() -if group then -local groupname=group:GetName() -DetectedGroups[groupname]=group -RecceGroups[groupname]=RecceDetecting[unitname] -end -end -self:CreateDetectedItems(DetectedGroups,RecceGroups) -if self.clusteranalysis then -self:PaintPicture() -end -end -function INTEL:CreateDetectedItems(DetectedGroups,RecceDetecting) -self:F({RecceDetecting=RecceDetecting}) -local Tnow=timer.getAbsTime() -for groupname,_group in pairs(DetectedGroups)do -local group=_group -local detecteditem=self:GetContactByName(groupname) -if detecteditem then -detecteditem.Tdetected=Tnow -detecteditem.position=group:GetCoordinate() -detecteditem.velocity=group:GetVelocityVec3() -detecteditem.speed=group:GetVelocityMPS() -else -local item={} -item.groupname=groupname -item.group=group -item.Tdetected=Tnow -item.typename=group:GetTypeName() -item.attribute=group:GetAttribute() -item.category=group:GetCategory() -item.categoryname=group:GetCategoryName() -item.threatlevel=group:GetThreatLevel() -item.position=group:GetCoordinate() -item.velocity=group:GetVelocityVec3() -item.speed=group:GetVelocityMPS() -item.recce=RecceDetecting[groupname] -self:T(string.format("%s group detect by %s/%s",groupname,RecceDetecting[groupname]or"unknonw",item.recce or"unknown")) -self:AddContact(item) -self:NewContact(item) -end -end -for i=#self.Contacts,1,-1 do -local item=self.Contacts[i] -if self:_CheckContactLost(item)then -self:LostContact(item) -self:RemoveContact(item) -end -end -end -function INTEL:GetDetectedUnits(Unit,DetectedUnits,RecceDetecting,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -local detectedtargets=Unit:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK) -local reccename=Unit:GetName() -for DetectionObjectID,Detection in pairs(detectedtargets or{})do -local DetectedObject=Detection.object -if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then -local unit=UNIT:Find(DetectedObject) -if unit and unit:IsAlive()then -local unitname=unit:GetName() -DetectedUnits[unitname]=unit -RecceDetecting[unitname]=reccename -self:T(string.format("Unit %s detect by %s",unitname,reccename)) -end -end -end -end -function INTEL:onafterNewContact(From,Event,To,Contact) -self:F(self.lid..string.format("NEW contact %s",Contact.groupname)) -table.insert(self.ContactsUnknown,Contact) -end -function INTEL:onafterLostContact(From,Event,To,Contact) -self:F(self.lid..string.format("LOST contact %s",Contact.groupname)) -table.insert(self.ContactsLost,Contact) -end -function INTEL:onafterNewCluster(From,Event,To,Contact,Cluster) -self:F(self.lid..string.format("NEW cluster %d size %d with contact %s",Cluster.index,Cluster.size,Contact.groupname)) -end -function INTEL:onafterLostCluster(From,Event,To,Cluster,Mission) -local text=self.lid..string.format("LOST cluster %d",Cluster.index) -if Mission then -local mission=Mission -text=text..string.format(" mission name=%s type=%s target=%s",mission.name,mission.type,mission:GetTargetName()or"unkown") -end -self:T(text) -end -function INTEL:GetContactByName(groupname) -for i,_contact in pairs(self.Contacts)do -local contact=_contact -if contact.groupname==groupname then -return contact -end -end -return nil -end -function INTEL:AddContact(Contact) -table.insert(self.Contacts,Contact) -end -function INTEL:RemoveContact(Contact) -for i,_contact in pairs(self.Contacts)do -local contact=_contact -if contact.groupname==Contact.groupname then -table.remove(self.Contacts,i) -end -end -end -function INTEL:_CheckContactLost(Contact) -if Contact.group==nil or not Contact.group:IsAlive()then -return true -end -local dT=timer.getAbsTime()-Contact.Tdetected -local dTforget=self.dTforget -if Contact.category==Group.Category.GROUND then -dTforget=60*60*2 -elseif Contact.category==Group.Category.AIRPLANE then -dTforget=60*10 -elseif Contact.category==Group.Category.HELICOPTER then -dTforget=60*20 -elseif Contact.category==Group.Category.SHIP then -dTforget=60*60 -elseif Contact.category==Group.Category.TRAIN then -dTforget=60*60 -end -if dT>dTforget then -return true -else -return false -end -end -function INTEL:PaintPicture() -for _,_contact in pairs(self.ContactsLost)do -local contact=_contact -local cluster=self:GetClusterOfContact(contact) -if cluster then -self:RemoveContactFromCluster(contact,cluster) -end -end -local ClusterSet={} -for _i,_cluster in pairs(self.Clusters)do -if(_cluster.size>0)and(self:ClusterCountUnits(_cluster)>0)then -table.insert(ClusterSet,_cluster) -else -local mission=_cluster.mission or nil -local marker=_cluster.marker -if marker then -marker:Remove() -end -self:LostCluster(_cluster,mission) -end -end -self.Clusters=ClusterSet -self:_UpdateClusterPositions() -for _,_contact in pairs(self.Contacts)do -local contact=_contact -self:T(string.format("Paint Picture: checking for %s",contact.groupname)) -local isincluster=self:CheckContactInClusters(contact) -local currentcluster=self:GetClusterOfContact(contact) -if currentcluster then -local isconnected=self:IsContactConnectedToCluster(contact,currentcluster) -if(not isconnected)and(currentcluster.size>1)then -local cluster=self:IsContactPartOfAnyClusters(contact) -if cluster then -self:AddContactToCluster(contact,cluster) -else -local newcluster=self:CreateCluster(contact.position) -self:AddContactToCluster(contact,newcluster) -self:NewCluster(contact,newcluster) -end -end -else -local cluster=self:IsContactPartOfAnyClusters(contact) -if cluster then -self:AddContactToCluster(contact,cluster) -else -local newcluster=self:CreateCluster(contact.position) -self:AddContactToCluster(contact,newcluster) -self:NewCluster(contact,newcluster) -end -end -end -if self.clustermarkers then -for _,_cluster in pairs(self.Clusters)do -local cluster=_cluster -local coordinate=self:GetClusterCoordinate(cluster) -self:UpdateClusterMarker(cluster) -end -end -end -function INTEL:CreateCluster(coordinate) -local cluster={} -cluster.index=self.clustercounter -cluster.coordinate=coordinate -cluster.threatlevelSum=0 -cluster.threatlevelMax=0 -cluster.size=0 -cluster.Contacts={} -table.insert(self.Clusters,cluster) -self.clustercounter=self.clustercounter+1 -return cluster -end -function INTEL:AddContactToCluster(contact,cluster) -if contact and cluster then -table.insert(cluster.Contacts,contact) -cluster.threatlevelSum=cluster.threatlevelSum+contact.threatlevel -cluster.size=cluster.size+1 -end -end -function INTEL:RemoveContactFromCluster(contact,cluster) -if contact and cluster then -for i,_contact in pairs(cluster.Contacts)do -local Contact=_contact -if Contact.groupname==contact.groupname then -cluster.threatlevelSum=cluster.threatlevelSum-contact.threatlevel -cluster.size=cluster.size-1 -table.remove(cluster.Contacts,i) -return -end -end -end -end -function INTEL:CalcClusterThreatlevelSum(cluster) -local threatlevel=0 -for _,_contact in pairs(cluster.Contacts)do -local contact=_contact -threatlevel=threatlevel+contact.threatlevel -end -cluster.threatlevelSum=threatlevel -return threatlevel -end -function INTEL:CalcClusterThreatlevelAverage(cluster) -local threatlevel=self:CalcClusterThreatlevelSum(cluster) -threatlevel=threatlevel/cluster.size -cluster.threatlevelAve=threatlevel -return threatlevel -end -function INTEL:CalcClusterThreatlevelMax(cluster) -local threatlevel=0 -for _,_contact in pairs(cluster.Contacts)do -local contact=_contact -if contact.threatlevel>threatlevel then -threatlevel=contact.threatlevel -end -end -cluster.threatlevelMax=threatlevel -return threatlevel -end -function INTEL:CheckContactInClusters(contact) -for _,_cluster in pairs(self.Clusters)do -local cluster=_cluster -for _,_contact in pairs(cluster.Contacts)do -local Contact=_contact -if Contact.groupname==contact.groupname then -return true -end -end -end -return false -end -function INTEL:IsContactConnectedToCluster(contact,cluster) -for _,_contact in pairs(cluster.Contacts)do -local Contact=_contact -if Contact.groupname~=contact.groupname then -local dist=Contact.position:DistanceFromPointVec2(contact.position) -local radius=self.clusterradius or 15 -if dist1000 then -return true -else -return false -end -end -function INTEL:_UpdateClusterPositions() -for _,_cluster in pairs(self.Clusters)do -local coord=self:GetClusterCoordinate(_cluster) -_cluster.coordinate=coord -self:T(self.lid..string.format("Cluster size: %s",_cluster.size)) -end -end -function INTEL:ClusterCountUnits(Cluster) -local unitcount=0 -for _,_group in pairs(Cluster.Contacts)do -unitcount=unitcount+_group.group:CountAliveUnits() -end -return unitcount -end -function INTEL:UpdateClusterMarker(cluster) -local unitcount=self:ClusterCountUnits(cluster) -local text=string.format("Cluster #%d. Size %d, Units %d, TLsum=%d",cluster.index,cluster.size,unitcount,cluster.threatlevelSum) -if not cluster.marker then -if self.coalition==coalition.side.RED then -cluster.marker=MARKER:New(cluster.coordinate,text):ToRed() -elseif self.coalition==coalition.side.BLUE then -cluster.marker=MARKER:New(cluster.coordinate,text):ToBlue() -else -cluster.marker=MARKER:New(cluster.coordinate,text):ToNeutral() -end -else -local refresh=false -if cluster.marker.text~=text then -cluster.marker.text=text -refresh=true -end -if cluster.marker.coordinate~=cluster.coordinate then -cluster.marker.coordinate=cluster.coordinate -refresh=true -end -if refresh then -cluster.marker:Refresh() -end -end -return self -end -AI_BALANCER={ -ClassName="AI_BALANCER", -PatrolZones={}, -AIGroups={}, -Earliest=5, -Latest=60, -} -function AI_BALANCER:New(SetClient,SpawnAI) -local self=BASE:Inherit(self,FSM_SET:New(SET_GROUP:New())) -self:SetStartState("None") -self:AddTransition("*","Monitor","Monitoring") -self:AddTransition("*","Spawn","Spawning") -self:AddTransition("Spawning","Spawned","Spawned") -self:AddTransition("*","Destroy","Destroying") -self:AddTransition("*","Return","Returning") -self.SetClient=SetClient -self.SetClient:FilterOnce() -self.SpawnAI=SpawnAI -self.SpawnQueue={} -self.ToNearestAirbase=false -self.ToHomeAirbase=false -self:__Monitor(1) -return self -end -function AI_BALANCER:InitSpawnInterval(Earliest,Latest) -self.Earliest=Earliest -self.Latest=Latest -return self -end -function AI_BALANCER:ReturnToNearestAirbases(ReturnThresholdRange,ReturnAirbaseSet) -self.ToNearestAirbase=true -self.ReturnThresholdRange=ReturnThresholdRange -self.ReturnAirbaseSet=ReturnAirbaseSet -end -function AI_BALANCER:ReturnToHomeAirbase(ReturnThresholdRange) -self.ToHomeAirbase=true -self.ReturnThresholdRange=ReturnThresholdRange -end -function AI_BALANCER:onenterSpawning(SetGroup,From,Event,To,ClientName) -local AIGroup=self.SpawnAI:Spawn() -if AIGroup then -AIGroup:T({"Spawning new AIGroup",ClientName=ClientName}) -SetGroup:Remove(ClientName) -SetGroup:Add(ClientName,AIGroup) -self.SpawnQueue[ClientName]=nil -self:Spawned(AIGroup) -end -end -function AI_BALANCER:onenterDestroying(SetGroup,From,Event,To,ClientName,AIGroup) -AIGroup:Destroy() -SetGroup:Flush(self) -SetGroup:Remove(ClientName) -SetGroup:Flush(self) -end -function AI_BALANCER:onenterReturning(SetGroup,From,Event,To,AIGroup) -local AIGroupTemplate=AIGroup:GetTemplate() -if self.ToHomeAirbase==true then -local WayPointCount=#AIGroupTemplate.route.points -local SwitchWayPointCommand=AIGroup:CommandSwitchWayPoint(1,WayPointCount,1) -AIGroup:SetCommand(SwitchWayPointCommand) -AIGroup:MessageToRed("Returning to home base ...",30) -else -local PointVec2=POINT_VEC2:New(AIGroup:GetVec2().x,AIGroup:GetVec2().y) -local ClosestAirbase=self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2(PointVec2) -self:T(ClosestAirbase.AirbaseName) -AIGroup:RouteRTB(ClosestAirbase) -end -end -function AI_BALANCER:onenterMonitoring(SetGroup) -self:T2({self.SetClient:Count()}) -self.SetClient:ForEachClient( -function(Client) -self:T3(Client.ClientName) -local AIGroup=self.Set:Get(Client.UnitName) -if AIGroup then self:T({AIGroup=AIGroup:GetName(),IsAlive=AIGroup:IsAlive()})end -if Client:IsAlive()==true then -if AIGroup and AIGroup:IsAlive()==true then -if self.ToNearestAirbase==false and self.ToHomeAirbase==false then -self:Destroy(Client.UnitName,AIGroup) -else -local PlayerInRange={Value=false} -local RangeZone=ZONE_RADIUS:New('RangeZone',AIGroup:GetVec2(),self.ReturnThresholdRange) -self:T2(RangeZone) -_DATABASE:ForEachPlayerUnit( -function(RangeTestUnit,RangeZone,AIGroup,PlayerInRange) -self:T2({PlayerInRange,RangeTestUnit.UnitName,RangeZone.ZoneName}) -if RangeTestUnit:IsInZone(RangeZone)==true then -self:T2("in zone") -if RangeTestUnit:GetCoalition()~=AIGroup:GetCoalition()then -self:T2("in range") -PlayerInRange.Value=true -end -end -end, -function(RangeZone,AIGroup,PlayerInRange) -if PlayerInRange.Value==false then -self:Return(AIGroup) -end -end -,RangeZone,AIGroup,PlayerInRange -) -end -self.Set:Remove(Client.UnitName) -end -else -if not AIGroup or not AIGroup:IsAlive()==true then -self:T("Client "..Client.UnitName.." not alive.") -self:T({Queue=self.SpawnQueue[Client.UnitName]}) -if not self.SpawnQueue[Client.UnitName]then -self:__Spawn(math.random(self.Earliest,self.Latest),Client.UnitName) -self.SpawnQueue[Client.UnitName]=true -self:T("New AI Spawned for Client "..Client.UnitName) -end -end -end -return true -end -) -self:__Monitor(10) -end -AI_AIR={ -ClassName="AI_AIR", -} -AI_AIR.TaskDelay=0.5 -function AI_AIR:New(AIGroup) -local self=BASE:Inherit(self,FSM_CONTROLLABLE:New()) -self:SetControllable(AIGroup) -self:SetStartState("Stopped") -self:AddTransition("*","Queue","Queued") -self:AddTransition("*","Start","Started") -self:AddTransition("*","Stop","Stopped") -self:AddTransition("*","Status","*") -self:AddTransition("*","RTB","*") -self:AddTransition("Patrolling","Refuel","Refuelling") -self:AddTransition("*","Takeoff","Airborne") -self:AddTransition("*","Return","Returning") -self:AddTransition("*","Hold","Holding") -self:AddTransition("*","Home","Home") -self:AddTransition("*","LostControl","LostControl") -self:AddTransition("*","Fuel","Fuel") -self:AddTransition("*","Damaged","Damaged") -self:AddTransition("*","Eject","*") -self:AddTransition("*","Crash","Crashed") -self:AddTransition("*","PilotDead","*") -self.IdleCount=0 -return self -end -function GROUP:OnEventTakeoff(EventData,Fsm) -Fsm:Takeoff() -self:UnHandleEvent(EVENTS.Takeoff) -end -function AI_AIR:SetDispatcher(Dispatcher) -self.Dispatcher=Dispatcher -end -function AI_AIR:GetDispatcher() -return self.Dispatcher -end -function AI_AIR:SetTargetDistance(Coordinate) -local CurrentCoord=self.Controllable:GetCoordinate() -self.TargetDistance=CurrentCoord:Get2DDistance(Coordinate) -self.ClosestTargetDistance=(not self.ClosestTargetDistance or self.ClosestTargetDistance>self.TargetDistance)and self.TargetDistance or self.ClosestTargetDistance -end -function AI_AIR:ClearTargetDistance() -self.TargetDistance=nil -self.ClosestTargetDistance=nil -end -function AI_AIR:SetSpeed(PatrolMinSpeed,PatrolMaxSpeed) -self:F2({PatrolMinSpeed,PatrolMaxSpeed}) -self.PatrolMinSpeed=PatrolMinSpeed -self.PatrolMaxSpeed=PatrolMaxSpeed -end -function AI_AIR:SetRTBSpeed(RTBMinSpeed,RTBMaxSpeed) -self:F({RTBMinSpeed,RTBMaxSpeed}) -self.RTBMinSpeed=RTBMinSpeed -self.RTBMaxSpeed=RTBMaxSpeed -end -function AI_AIR:SetAltitude(PatrolFloorAltitude,PatrolCeilingAltitude) -self:F2({PatrolFloorAltitude,PatrolCeilingAltitude}) -self.PatrolFloorAltitude=PatrolFloorAltitude -self.PatrolCeilingAltitude=PatrolCeilingAltitude -end -function AI_AIR:SetHomeAirbase(HomeAirbase) -self:F2({HomeAirbase}) -self.HomeAirbase=HomeAirbase -end -function AI_AIR:SetTanker(TankerName) -self:F2({TankerName}) -self.TankerName=TankerName -end -function AI_AIR:SetDisengageRadius(DisengageRadius) -self:F2({DisengageRadius}) -self.DisengageRadius=DisengageRadius -end -function AI_AIR:SetStatusOff() -self:F2() -self.CheckStatus=false -end -function AI_AIR:SetFuelThreshold(FuelThresholdPercentage,OutOfFuelOrbitTime) -self.FuelThresholdPercentage=FuelThresholdPercentage -self.OutOfFuelOrbitTime=OutOfFuelOrbitTime -self.Controllable:OptionRTBBingoFuel(false) -return self -end -function AI_AIR:SetDamageThreshold(PatrolDamageThreshold) -self.PatrolManageDamage=true -self.PatrolDamageThreshold=PatrolDamageThreshold -return self -end -function AI_AIR:onafterStart(Controllable,From,Event,To) -self:__Status(10) -self:HandleEvent(EVENTS.PilotDead,self.OnPilotDead) -self:HandleEvent(EVENTS.Crash,self.OnCrash) -self:HandleEvent(EVENTS.Ejection,self.OnEjection) -Controllable:OptionROEHoldFire() -Controllable:OptionROTVertical() -end -function AI_AIR:onafterReturn(Controllable,From,Event,To) -self:__RTB(self.TaskDelay) -end -function AI_AIR:onbeforeStatus() -return self.CheckStatus -end -function AI_AIR:onafterStatus() -if self.Controllable and self.Controllable:IsAlive()then -local RTB=false -local DistanceFromHomeBase=self.HomeAirbase:GetCoordinate():Get2DDistance(self.Controllable:GetCoordinate()) -if not self:Is("Holding")and not self:Is("Returning")then -local DistanceFromHomeBase=self.HomeAirbase:GetCoordinate():Get2DDistance(self.Controllable:GetCoordinate()) -if DistanceFromHomeBase>self.DisengageRadius then -self:I(self.Controllable:GetName().." is too far from home base, RTB!") -self:Hold(300) -RTB=false -end -end -if not self:Is("Fuel")and not self:Is("Home")and not self:is("Refuelling")then -local Fuel=self.Controllable:GetFuelMin() -if Fuel=10 then -if Damage~=InitialLife then -self:Damaged() -else -self:I(self.Controllable:GetName().." control lost! ") -self:LostControl() -end -else -self.IdleCount=self.IdleCount+1 -end -end -else -self.IdleCount=0 -end -if RTB==true then -self:__RTB(self.TaskDelay) -end -if not self:Is("Home")then -self:__Status(10) -end -end -end -function AI_AIR.RTBRoute(AIGroup,Fsm) -AIGroup:F({"AI_AIR.RTBRoute:",AIGroup:GetName()}) -if AIGroup:IsAlive()then -Fsm:RTB() -end -end -function AI_AIR.RTBHold(AIGroup,Fsm) -AIGroup:F({"AI_AIR.RTBHold:",AIGroup:GetName()}) -if AIGroup:IsAlive()then -Fsm:__RTB(Fsm.TaskDelay) -Fsm:Return() -local Task=AIGroup:TaskOrbitCircle(4000,400) -AIGroup:SetTask(Task) -end -end -function AI_AIR:onafterRTB(AIGroup,From,Event,To) -self:F({AIGroup,From,Event,To}) -if AIGroup and AIGroup:IsAlive()then -self:I("Group "..AIGroup:GetName().." ... RTB! ( "..self:GetState().." )") -self:ClearTargetDistance() -local EngageRoute={} -local FromCoord=AIGroup:GetCoordinate() -local ToTargetCoord=self.HomeAirbase:GetCoordinate() -local ToTargetVec3=ToTargetCoord:GetVec3() -ToTargetVec3.y=ToTargetCoord:GetLandHeight()+1000 -local ToTargetCoord2=COORDINATE:NewFromVec3(ToTargetVec3) -if not self.RTBMinSpeed or not self.RTBMaxSpeed then -local RTBSpeedMax=AIGroup:GetSpeedMax() -self:SetRTBSpeed(RTBSpeedMax*0.5,RTBSpeedMax*0.6) -end -local RTBSpeed=math.random(self.RTBMinSpeed,self.RTBMaxSpeed) -local Distance=FromCoord:Get2DDistance(ToTargetCoord2) -local ToAirbaseCoord=ToTargetCoord2 -if Distance<5000 then -self:I("RTB and near the airbase!") -self:Home() -return -end -if not AIGroup:InAir()==true then -self:I("Not anymore in the air, considered Home.") -self:Home() -return -end -local FromRTBRoutePoint=FromCoord:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -RTBSpeed, -true -) -local ToRTBRoutePoint=ToAirbaseCoord:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -RTBSpeed, -true -) -EngageRoute[#EngageRoute+1]=FromRTBRoutePoint -EngageRoute[#EngageRoute+1]=ToRTBRoutePoint -local Tasks={} -Tasks[#Tasks+1]=AIGroup:TaskFunction("AI_AIR.RTBRoute",self) -EngageRoute[#EngageRoute].task=AIGroup:TaskCombo(Tasks) -AIGroup:OptionROEHoldFire() -AIGroup:OptionROTEvadeFire() -AIGroup:Route(EngageRoute,self.TaskDelay) -end -end -function AI_AIR:onafterHome(AIGroup,From,Event,To) -self:F({AIGroup,From,Event,To}) -self:I("Group "..self.Controllable:GetName().." ... Home! ( "..self:GetState().." )") -if AIGroup and AIGroup:IsAlive()then -end -end -function AI_AIR:onafterHold(AIGroup,From,Event,To,HoldTime) -self:F({AIGroup,From,Event,To}) -self:I("Group "..self.Controllable:GetName().." ... Holding! ( "..self:GetState().." )") -if AIGroup and AIGroup:IsAlive()then -local OrbitTask=AIGroup:TaskOrbitCircle(math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude),self.PatrolMinSpeed) -local TimedOrbitTask=AIGroup:TaskControlled(OrbitTask,AIGroup:TaskCondition(nil,nil,nil,nil,HoldTime,nil)) -local RTBTask=AIGroup:TaskFunction("AI_AIR.RTBHold",self) -local OrbitHoldTask=AIGroup:TaskOrbitCircle(4000,self.PatrolMinSpeed) -AIGroup:SetTask(AIGroup:TaskCombo({TimedOrbitTask,RTBTask,OrbitHoldTask}),1) -end -end -function AI_AIR.Resume(AIGroup,Fsm) -AIGroup:I({"AI_AIR.Resume:",AIGroup:GetName()}) -if AIGroup:IsAlive()then -Fsm:__RTB(Fsm.TaskDelay) -end -end -function AI_AIR:onafterRefuel(AIGroup,From,Event,To) -self:F({AIGroup,From,Event,To}) -if AIGroup and AIGroup:IsAlive()then -local Tanker=GROUP:FindByName(self.TankerName) -if Tanker and Tanker:IsAlive()and Tanker:IsAirPlane()then -self:I("Group "..self.Controllable:GetName().." ... Refuelling! State="..self:GetState()..", Refuelling tanker "..self.TankerName) -local RefuelRoute={} -local FromRefuelCoord=AIGroup:GetCoordinate() -local ToRefuelCoord=Tanker:GetCoordinate() -local ToRefuelSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) -local FromRefuelRoutePoint=FromRefuelCoord:WaypointAir(self.PatrolAltType,POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToRefuelSpeed,true) -local ToRefuelRoutePoint=Tanker:GetCoordinate():WaypointAir(self.PatrolAltType,POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToRefuelSpeed,true) -self:F({ToRefuelSpeed=ToRefuelSpeed}) -RefuelRoute[#RefuelRoute+1]=FromRefuelRoutePoint -RefuelRoute[#RefuelRoute+1]=ToRefuelRoutePoint -AIGroup:OptionROEHoldFire() -AIGroup:OptionROTEvadeFire() -local classname=self:GetClassName() -if classname=="AI_A2A_CAP"then -classname="AI_AIR_PATROL" -end -env.info("FF refueling classname="..classname) -local Tasks={} -Tasks[#Tasks+1]=AIGroup:TaskRefueling() -Tasks[#Tasks+1]=AIGroup:TaskFunction(classname..".Resume",self) -RefuelRoute[#RefuelRoute].task=AIGroup:TaskCombo(Tasks) -AIGroup:Route(RefuelRoute,self.TaskDelay) -else -self:RTB() -end -end -end -function AI_AIR:onafterDead() -self:SetStatusOff() -end -function AI_AIR:OnCrash(EventData) -if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then -if#self.Controllable:GetUnits()==1 then -self:__Crash(self.TaskDelay,EventData) -end -end -end -function AI_AIR:OnEjection(EventData) -if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then -self:__Eject(self.TaskDelay,EventData) -end -end -function AI_AIR:OnPilotDead(EventData) -if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then -self:__PilotDead(self.TaskDelay,EventData) -end -end -AI_AIR_PATROL={ -ClassName="AI_AIR_PATROL", -} -function AI_AIR_PATROL:New(AI_Air,AIGroup,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType) -local self=BASE:Inherit(self,AI_Air) -local SpeedMax=AIGroup:GetSpeedMax() -self.PatrolZone=PatrolZone -self.PatrolFloorAltitude=PatrolFloorAltitude or 1000 -self.PatrolCeilingAltitude=PatrolCeilingAltitude or 1500 -self.PatrolMinSpeed=PatrolMinSpeed or SpeedMax*0.5 -self.PatrolMaxSpeed=PatrolMaxSpeed or SpeedMax*0.75 -self.PatrolAltType=PatrolAltType or"RADIO" -self:AddTransition({"Started","Airborne","Refuelling"},"Patrol","Patrolling") -self:AddTransition("Patrolling","PatrolRoute","Patrolling") -self:AddTransition("*","Reset","Patrolling") -return self -end -function AI_AIR_PATROL:SetEngageRange(EngageRange) -self:F2() -if EngageRange then -self.EngageRange=EngageRange -else -self.EngageRange=nil -end -end -function AI_AIR_PATROL:SetRaceTrackPattern(LegMin,LegMax,HeadingMin,HeadingMax,DurationMin,DurationMax,CapCoordinates) -self.racetrack=true -self.racetracklegmin=LegMin or 10000 -self.racetracklegmax=LegMax or 15000 -self.racetrackheadingmin=HeadingMin or 0 -self.racetrackheadingmax=HeadingMax or 180 -self.racetrackdurationmin=DurationMin -self.racetrackdurationmax=DurationMax -if self.racetrackdurationmax and not self.racetrackdurationmin then -self.racetrackdurationmin=self.racetrackdurationmax -end -self.racetrackcapcoordinates=CapCoordinates -end -function AI_AIR_PATROL:onafterPatrol(AIPatrol,From,Event,To) -self:F2() -self:ClearTargetDistance() -self:__PatrolRoute(self.TaskDelay) -AIPatrol:OnReSpawn( -function(PatrolGroup) -self:__Reset(self.TaskDelay) -self:__PatrolRoute(self.TaskDelay) -end -) -end -function AI_AIR_PATROL.___PatrolRoute(AIPatrol,Fsm) -AIPatrol:F({"AI_AIR_PATROL.___PatrolRoute:",AIPatrol:GetName()}) -if AIPatrol and AIPatrol:IsAlive()then -Fsm:PatrolRoute() -end -end -function AI_AIR_PATROL:onafterPatrolRoute(AIPatrol,From,Event,To) -self:F2() -if From=="RTB"then -return -end -if AIPatrol and AIPatrol:IsAlive()then -local PatrolRoute={} -local CurrentCoord=AIPatrol:GetCoordinate() -local altitude=math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude) -local ToTargetCoord=self.PatrolZone:GetRandomPointVec2() -ToTargetCoord:SetAlt(altitude) -self:SetTargetDistance(ToTargetCoord) -local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) -local speedkmh=ToTargetSpeed -local FromWP=CurrentCoord:WaypointAir(self.PatrolAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToTargetSpeed,true) -PatrolRoute[#PatrolRoute+1]=FromWP -if self.racetrack then -local heading=math.random(self.racetrackheadingmin,self.racetrackheadingmax) -local leg=math.random(self.racetracklegmin,self.racetracklegmax) -local duration=self.racetrackdurationmin -if self.racetrackdurationmax then -duration=math.random(self.racetrackdurationmin,self.racetrackdurationmax) -end -local c0=self.PatrolZone:GetRandomCoordinate() -if self.racetrackcapcoordinates and#self.racetrackcapcoordinates>0 then -c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)] -end -local c1=c0:SetAltitude(altitude) -local c2=c1:Translate(leg,heading):SetAltitude(altitude) -self:SetTargetDistance(c0) -self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec",UTILS.KmphToKnots(speedkmh),UTILS.MetersToFeet(altitude),heading,leg,tostring(duration))) -local taskOrbit=AIPatrol:TaskOrbit(c1,altitude,UTILS.KmphToMps(speedkmh),c2) -local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute",self) -local taskCond=AIPatrol:TaskCondition(nil,nil,nil,nil,duration,nil) -local taskCont=AIPatrol:TaskControlled(taskOrbit,taskCond) -PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType,speedkmh,{taskCont,taskPatrol},"CAP Orbit") -else -local ToWP=ToTargetCoord:WaypointAir(self.PatrolAltType,POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToTargetSpeed,true) -PatrolRoute[#PatrolRoute+1]=ToWP -local Tasks={} -Tasks[#Tasks+1]=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute",self) -PatrolRoute[#PatrolRoute].task=AIPatrol:TaskCombo(Tasks) -end -AIPatrol:OptionROEReturnFire() -AIPatrol:OptionROTEvadeFire() -AIPatrol:Route(PatrolRoute,self.TaskDelay) -end -end -function AI_AIR_PATROL.Resume(AIPatrol,Fsm) -AIPatrol:F({"AI_AIR_PATROL.Resume:",AIPatrol:GetName()}) -if AIPatrol and AIPatrol:IsAlive()then -Fsm:__Reset(Fsm.TaskDelay) -Fsm:__PatrolRoute(Fsm.TaskDelay) -end -end -AI_AIR_ENGAGE={ -ClassName="AI_AIR_ENGAGE", -} -function AI_AIR_ENGAGE:New(AI_Air,AIGroup,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local self=BASE:Inherit(self,AI_Air) -self.Accomplished=false -self.Engaging=false -local SpeedMax=AIGroup:GetSpeedMax() -self.EngageMinSpeed=EngageMinSpeed or SpeedMax*0.5 -self.EngageMaxSpeed=EngageMaxSpeed or SpeedMax*0.75 -self.EngageFloorAltitude=EngageFloorAltitude or 1000 -self.EngageCeilingAltitude=EngageCeilingAltitude or 1500 -self.EngageAltType=EngageAltType or"RADIO" -self:AddTransition({"Started","Engaging","Returning","Airborne","Patrolling"},"EngageRoute","Engaging") -self:AddTransition({"Started","Engaging","Returning","Airborne","Patrolling"},"Engage","Engaging") -self:AddTransition("Engaging","Fired","Engaging") -self:AddTransition("*","Destroy","*") -self:AddTransition("Engaging","Abort","Patrolling") -self:AddTransition("Engaging","Accomplish","Patrolling") -self:AddTransition({"Patrolling","Engaging"},"Refuel","Refuelling") -return self -end -function AI_AIR_ENGAGE:onafterStart(AIGroup,From,Event,To) -self:GetParent(self,AI_AIR_ENGAGE).onafterStart(self,AIGroup,From,Event,To) -AIGroup:HandleEvent(EVENTS.Takeoff,nil,self) -end -function AI_AIR_ENGAGE:onafterEngage(AIGroup,From,Event,To) -self:HandleEvent(EVENTS.Dead) -end -function AI_AIR_ENGAGE:onbeforeEngage(AIGroup,From,Event,To) -if self.Accomplished==true then -return false -end -return true -end -function AI_AIR_ENGAGE:onafterAbort(AIGroup,From,Event,To) -AIGroup:ClearTasks() -self:Return() -end -function AI_AIR_ENGAGE:onafterAccomplish(AIGroup,From,Event,To) -self.Accomplished=true -end -function AI_AIR_ENGAGE:onafterDestroy(AIGroup,From,Event,To,EventData) -if EventData.IniUnit then -self.AttackUnits[EventData.IniUnit]=nil -end -end -function AI_AIR_ENGAGE:OnEventDead(EventData) -self:F({"EventDead",EventData}) -if EventData.IniDCSUnit then -if self.AttackUnits and self.AttackUnits[EventData.IniUnit]then -self:__Destroy(self.TaskDelay,EventData) -end -end -end -function AI_AIR_ENGAGE.___EngageRoute(AIGroup,Fsm,AttackSetUnit) -Fsm:I(string.format("AI_AIR_ENGAGE.___EngageRoute: %s",tostring(AIGroup:GetName()))) -if AIGroup and AIGroup:IsAlive()then -Fsm:__EngageRoute(Fsm.TaskDelay or 0.1,AttackSetUnit) -end -end -function AI_AIR_ENGAGE:onafterEngageRoute(DefenderGroup,From,Event,To,AttackSetUnit) -self:I({DefenderGroup,From,Event,To,AttackSetUnit}) -local DefenderGroupName=DefenderGroup:GetName() -self.AttackSetUnit=AttackSetUnit -local AttackCount=AttackSetUnit:CountAlive() -if AttackCount>0 then -if DefenderGroup:IsAlive()then -local EngageAltitude=math.random(self.EngageFloorAltitude,self.EngageCeilingAltitude) -local EngageSpeed=math.random(self.EngageMinSpeed,self.EngageMaxSpeed) -local DefenderCoord=DefenderGroup:GetPointVec3() -DefenderCoord:SetY(EngageAltitude) -local TargetCoord=AttackSetUnit:GetFirst():GetPointVec3() -TargetCoord:SetY(EngageAltitude) -local TargetDistance=DefenderCoord:Get2DDistance(TargetCoord) -local EngageDistance=(DefenderGroup:IsHelicopter()and 5000)or(DefenderGroup:IsAirPlane()and 10000) -if TargetDistance<=EngageDistance*9 then -self:I(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km",TargetDistance/1000)) -self:__Engage(0.1,AttackSetUnit) -else -self:I(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km",TargetDistance/1000)) -local EngageRoute={} -local AttackTasks={} -local FromWP=DefenderCoord:WaypointAir(self.PatrolAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true) -EngageRoute[#EngageRoute+1]=FromWP -self:SetTargetDistance(TargetCoord) -local FromEngageAngle=DefenderCoord:GetAngleDegrees(DefenderCoord:GetDirectionVec3(TargetCoord)) -local ToCoord=DefenderCoord:Translate(EngageDistance,FromEngageAngle,true) -local ToWP=ToCoord:WaypointAir(self.PatrolAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true) -EngageRoute[#EngageRoute+1]=ToWP -AttackTasks[#AttackTasks+1]=DefenderGroup:TaskFunction("AI_AIR_ENGAGE.___EngageRoute",self,AttackSetUnit) -EngageRoute[#EngageRoute].task=DefenderGroup:TaskCombo(AttackTasks) -DefenderGroup:OptionROEReturnFire() -DefenderGroup:OptionROTEvadeFire() -DefenderGroup:Route(EngageRoute,self.TaskDelay or 0.1) -end -end -else -self:I(DefenderGroupName..": No targets found -> Going RTB") -self:Return() -end -end -function AI_AIR_ENGAGE.___Engage(AIGroup,Fsm,AttackSetUnit) -Fsm:I(string.format("AI_AIR_ENGAGE.___Engage: %s",tostring(AIGroup:GetName()))) -if AIGroup and AIGroup:IsAlive()then -local delay=Fsm.TaskDelay or 0.1 -Fsm:__Engage(delay,AttackSetUnit) -end -end -function AI_AIR_ENGAGE:onafterEngage(DefenderGroup,From,Event,To,AttackSetUnit) -self:F({DefenderGroup,From,Event,To,AttackSetUnit}) -local DefenderGroupName=DefenderGroup:GetName() -self.AttackSetUnit=AttackSetUnit -local AttackCount=AttackSetUnit:CountAlive() -self:T({AttackCount=AttackCount}) -if AttackCount>0 then -if DefenderGroup and DefenderGroup:IsAlive()then -local EngageAltitude=math.random(self.EngageFloorAltitude or 500,self.EngageCeilingAltitude or 1000) -local EngageSpeed=math.random(self.EngageMinSpeed,self.EngageMaxSpeed) -local DefenderCoord=DefenderGroup:GetPointVec3() -DefenderCoord:SetY(EngageAltitude) -local TargetCoord=AttackSetUnit:GetFirst():GetPointVec3() -TargetCoord:SetY(EngageAltitude) -local TargetDistance=DefenderCoord:Get2DDistance(TargetCoord) -local EngageDistance=(DefenderGroup:IsHelicopter()and 5000)or(DefenderGroup:IsAirPlane()and 10000) -local EngageRoute={} -local AttackTasks={} -local FromWP=DefenderCoord:WaypointAir(self.EngageAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true) -EngageRoute[#EngageRoute+1]=FromWP -self:SetTargetDistance(TargetCoord) -local FromEngageAngle=DefenderCoord:GetAngleDegrees(DefenderCoord:GetDirectionVec3(TargetCoord)) -local ToCoord=DefenderCoord:Translate(EngageDistance,FromEngageAngle,true) -local ToWP=ToCoord:WaypointAir(self.EngageAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true) -EngageRoute[#EngageRoute+1]=ToWP -if TargetDistance<=EngageDistance*9 then -local AttackUnitTasks=self:CreateAttackUnitTasks(AttackSetUnit,DefenderGroup,EngageAltitude) -if#AttackUnitTasks==0 then -self:I(DefenderGroupName..": No valid targets found -> Going RTB") -self:Return() -return -else -local text=string.format("%s: Engaging targets at distance %.2f NM",DefenderGroupName,UTILS.MetersToNM(TargetDistance)) -self:I(text) -DefenderGroup:OptionROEOpenFire() -DefenderGroup:OptionROTEvadeFire() -DefenderGroup:OptionKeepWeaponsOnThreat() -AttackTasks[#AttackTasks+1]=DefenderGroup:TaskCombo(AttackUnitTasks) -end -end -AttackTasks[#AttackTasks+1]=DefenderGroup:TaskFunction("AI_AIR_ENGAGE.___Engage",self,AttackSetUnit) -EngageRoute[#EngageRoute].task=DefenderGroup:TaskCombo(AttackTasks) -DefenderGroup:Route(EngageRoute,self.TaskDelay or 0.1) -end -else -self:I(DefenderGroupName..": No targets found -> returning.") -self:Return() -return -end -end -function AI_AIR_ENGAGE.Resume(AIEngage,Fsm) -AIEngage:F({"Resume:",AIEngage:GetName()}) -if AIEngage and AIEngage:IsAlive()then -Fsm:__Reset(Fsm.TaskDelay or 0.1) -Fsm:__EngageRoute(Fsm.TaskDelay or 0.2,Fsm.AttackSetUnit) -end -end -AI_A2A_PATROL={ -ClassName="AI_A2A_PATROL", -} -function AI_A2A_PATROL:New(AIPatrol,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType) -local AI_Air=AI_AIR:New(AIPatrol) -local AI_Air_Patrol=AI_AIR_PATROL:New(AI_Air,AIPatrol,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType) -local self=BASE:Inherit(self,AI_Air_Patrol) -self:SetFuelThreshold(.2,60) -self:SetDamageThreshold(0.4) -self:SetDisengageRadius(70000) -self.PatrolZone=PatrolZone -self.PatrolFloorAltitude=PatrolFloorAltitude -self.PatrolCeilingAltitude=PatrolCeilingAltitude -self.PatrolMinSpeed=PatrolMinSpeed -self.PatrolMaxSpeed=PatrolMaxSpeed -self.PatrolAltType=PatrolAltType or"BARO" -self:AddTransition({"Started","Airborne","Refuelling"},"Patrol","Patrolling") -self:AddTransition("Patrolling","Route","Patrolling") -self:AddTransition("*","Reset","Patrolling") -return self -end -function AI_A2A_PATROL:SetSpeed(PatrolMinSpeed,PatrolMaxSpeed) -self:F2({PatrolMinSpeed,PatrolMaxSpeed}) -self.PatrolMinSpeed=PatrolMinSpeed -self.PatrolMaxSpeed=PatrolMaxSpeed -end -function AI_A2A_PATROL:SetAltitude(PatrolFloorAltitude,PatrolCeilingAltitude) -self:F2({PatrolFloorAltitude,PatrolCeilingAltitude}) -self.PatrolFloorAltitude=PatrolFloorAltitude -self.PatrolCeilingAltitude=PatrolCeilingAltitude -end -function AI_A2A_PATROL:onafterPatrol(AIPatrol,From,Event,To) -self:F2() -self:ClearTargetDistance() -self:__Route(1) -AIPatrol:OnReSpawn( -function(PatrolGroup) -self:__Reset(1) -self:__Route(5) -end -) -end -function AI_A2A_PATROL.PatrolRoute(AIPatrol,Fsm) -AIPatrol:F({"AI_A2A_PATROL.PatrolRoute:",AIPatrol:GetName()}) -if AIPatrol and AIPatrol:IsAlive()then -Fsm:Route() -end -end -function AI_A2A_PATROL:onafterRoute(AIPatrol,From,Event,To) -self:F2() -if From=="RTB"then -return -end -if AIPatrol and AIPatrol:IsAlive()then -local PatrolRoute={} -local CurrentCoord=AIPatrol:GetCoordinate() -local altitude=math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude) -local speedkmh=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) -PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil,speedkmh,{},"Current") -if self.racetrack then -local heading=math.random(self.racetrackheadingmin,self.racetrackheadingmax) -local leg=math.random(self.racetracklegmin,self.racetracklegmax) -local duration=self.racetrackdurationmin -if self.racetrackdurationmax then -duration=math.random(self.racetrackdurationmin,self.racetrackdurationmax) -end -local c0=self.PatrolZone:GetRandomCoordinate() -if self.racetrackcapcoordinates and#self.racetrackcapcoordinates>0 then -c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)] -end -local c1=c0:SetAltitude(altitude) -local c2=c1:Translate(leg,heading):SetAltitude(altitude) -self:SetTargetDistance(c0) -self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec",UTILS.KmphToKnots(speedkmh),UTILS.MetersToFeet(altitude),heading,leg,tostring(duration))) -local taskOrbit=AIPatrol:TaskOrbit(c1,altitude,UTILS.KmphToMps(speedkmh),c2) -local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute",self) -local taskCond=AIPatrol:TaskCondition(nil,nil,nil,nil,duration,nil) -local taskCont=AIPatrol:TaskControlled(taskOrbit,taskCond) -PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType,speedkmh,{taskCont,taskPatrol},"CAP Orbit") -else -local ToTargetCoord=self.PatrolZone:GetRandomCoordinate() -ToTargetCoord:SetAltitude(altitude) -self:SetTargetDistance(ToTargetCoord) -local taskReRoute=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute",self) -PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType,speedkmh,{taskReRoute},"Patrol Point") -end -AIPatrol:OptionROEReturnFire() -AIPatrol:OptionROTEvadeFire() -AIPatrol:Route(PatrolRoute,0.5) -end -end -AI_A2A_CAP={ -ClassName="AI_A2A_CAP", -} -function AI_A2A_CAP:New2(AICap,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType,PatrolZone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType) -local AI_Air=AI_AIR:New(AICap) -local AI_Air_Patrol=AI_AIR_PATROL:New(AI_Air,AICap,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType) -local AI_Air_Engage=AI_AIR_ENGAGE:New(AI_Air_Patrol,AICap,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local self=BASE:Inherit(self,AI_Air_Engage) -self:SetFuelThreshold(.2,60) -self:SetDamageThreshold(0.4) -self:SetDisengageRadius(70000) -return self -end -function AI_A2A_CAP:New(AICap,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,PatrolAltType) -return self:New2(AICap,EngageMinSpeed,EngageMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,PatrolZone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType) -end -function AI_A2A_CAP:onafterStart(AICap,From,Event,To) -self:GetParent(self,AI_A2A_CAP).onafterStart(self,AICap,From,Event,To) -AICap:HandleEvent(EVENTS.Takeoff,nil,self) -end -function AI_A2A_CAP:SetEngageZone(EngageZone) -self:F2() -if EngageZone then -self.EngageZone=EngageZone -else -self.EngageZone=nil -end -end -function AI_A2A_CAP:SetEngageRange(EngageRange) -self:F2() -if EngageRange then -self.EngageRange=EngageRange -else -self.EngageRange=nil -end -end -function AI_A2A_CAP:CreateAttackUnitTasks(AttackSetUnit,DefenderGroup,EngageAltitude) -local AttackUnitTasks={} -for AttackUnitID,AttackUnit in pairs(self.AttackSetUnit:GetSet())do -local AttackUnit=AttackUnit -if AttackUnit and AttackUnit:IsAlive()and AttackUnit:IsAir()then -self:T({"Attacking Task:",AttackUnit:GetName(),AttackUnit:IsAlive(),AttackUnit:IsAir()}) -AttackUnitTasks[#AttackUnitTasks+1]=DefenderGroup:TaskAttackUnit(AttackUnit) -end -end -return AttackUnitTasks -end -AI_A2A_GCI={ -ClassName="AI_A2A_GCI", -} -function AI_A2A_GCI:New2(AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local AI_Air=AI_AIR:New(AIIntercept) -local AI_Air_Engage=AI_AIR_ENGAGE:New(AI_Air,AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local self=BASE:Inherit(self,AI_Air_Engage) -self:SetFuelThreshold(.2,60) -self:SetDamageThreshold(0.4) -self:SetDisengageRadius(70000) -return self -end -function AI_A2A_GCI:New(AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -return self:New2(AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -end -function AI_A2A_GCI:onafterStart(AIIntercept,From,Event,To) -self:GetParent(self,AI_A2A_GCI).onafterStart(self,AIIntercept,From,Event,To) -end -function AI_A2A_GCI:CreateAttackUnitTasks(AttackSetUnit,DefenderGroup,EngageAltitude) -local AttackUnitTasks={} -for AttackUnitID,AttackUnit in pairs(self.AttackSetUnit:GetSet())do -local AttackUnit=AttackUnit -self:T({"Attacking Unit:",AttackUnit:GetName(),AttackUnit:IsAlive(),AttackUnit:IsAir()}) -if AttackUnit:IsAlive()and AttackUnit:IsAir()then -AttackUnitTasks[#AttackUnitTasks+1]=DefenderGroup:TaskAttackUnit(AttackUnit) -end -end -return AttackUnitTasks -end -do -AI_A2A_DISPATCHER={ -ClassName="AI_A2A_DISPATCHER", -Detection=nil, -} -AI_A2A_DISPATCHER.Takeoff=GROUP.Takeoff -AI_A2A_DISPATCHER.Landing={ -NearAirbase=1, -AtRunway=2, -AtEngineShutdown=3, -} -function AI_A2A_DISPATCHER:New(Detection) -local self=BASE:Inherit(self,DETECTION_MANAGER:New(nil,Detection)) -self.Detection=Detection -self.DefenderSquadrons={} -self.DefenderSpawns={} -self.DefenderTasks={} -self.DefenderDefault={} -self.SetSendPlayerMessages=false -self.Detection:FilterCategories({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER}) -self.Detection:SetRefreshTimeInterval(30) -self:SetEngageRadius() -self:SetGciRadius() -self:SetIntercept(300) -self:SetDisengageRadius(300000) -self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Air) -self:SetDefaultTakeoffInAirAltitude(500) -self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.NearAirbase) -self:SetDefaultOverhead(1) -self:SetDefaultGrouping(1) -self:SetDefaultFuelThreshold(0.15,0) -self:SetDefaultDamageThreshold(0.4) -self:SetDefaultCapTimeInterval(180,600) -self:SetDefaultCapLimit(1) -self:AddTransition("Started","Assign","Started") -self:AddTransition("*","CAP","*") -self:AddTransition("*","GCI","*") -self:AddTransition("*","ENGAGE","*") -self:HandleEvent(EVENTS.Crash,self.OnEventCrashOrDead) -self:HandleEvent(EVENTS.Dead,self.OnEventCrashOrDead) -self:HandleEvent(EVENTS.Land) -self:HandleEvent(EVENTS.EngineShutdown) -self:HandleEvent(EVENTS.BaseCaptured) -self:SetTacticalDisplay(false) -self.DefenderCAPIndex=0 -self:__Start(5) -return self -end -function AI_A2A_DISPATCHER:onafterStart(From,Event,To) -self:GetParent(self,AI_A2A_DISPATCHER).onafterStart(self,From,Event,To) -for SquadronName,_DefenderSquadron in pairs(self.DefenderSquadrons)do -local DefenderSquadron=_DefenderSquadron -DefenderSquadron.Resources={} -if DefenderSquadron.ResourceCount then -for Resource=1,DefenderSquadron.ResourceCount do -self:ParkDefender(DefenderSquadron) -end -end -end -end -function AI_A2A_DISPATCHER:ParkDefender(DefenderSquadron) -local TemplateID=math.random(1,#DefenderSquadron.Spawn) -local Spawn=DefenderSquadron.Spawn[TemplateID] -Spawn:InitGrouping(1) -local SpawnGroup -if self:IsSquadronVisible(DefenderSquadron.Name)then -local Grouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping -Grouping=1 -Spawn:InitGrouping(Grouping) -SpawnGroup=Spawn:SpawnAtAirbase(DefenderSquadron.Airbase,SPAWN.Takeoff.Cold) -local GroupName=SpawnGroup:GetName() -DefenderSquadron.Resources=DefenderSquadron.Resources or{} -DefenderSquadron.Resources[TemplateID]=DefenderSquadron.Resources[TemplateID]or{} -DefenderSquadron.Resources[TemplateID][GroupName]={} -DefenderSquadron.Resources[TemplateID][GroupName]=SpawnGroup -self.uncontrolled=self.uncontrolled or{} -self.uncontrolled[DefenderSquadron.Name]=self.uncontrolled[DefenderSquadron.Name]or{} -table.insert(self.uncontrolled[DefenderSquadron.Name],{group=SpawnGroup,name=GroupName,grouping=Grouping}) -end -end -function AI_A2A_DISPATCHER:OnEventBaseCaptured(EventData) -local AirbaseName=EventData.PlaceName -self:I("Captured "..AirbaseName) -for SquadronName,Squadron in pairs(self.DefenderSquadrons)do -if Squadron.AirbaseName==AirbaseName then -Squadron.ResourceCount=-999 -Squadron.Captured=true -self:I("Squadron "..SquadronName.." captured.") -end -end -end -function AI_A2A_DISPATCHER:OnEventCrashOrDead(EventData) -self.Detection:ForgetDetectedUnit(EventData.IniUnitName) -end -function AI_A2A_DISPATCHER:OnEventLand(EventData) -self:F("Landed") -local DefenderUnit=EventData.IniUnit -local Defender=EventData.IniGroup -local Squadron=self:GetSquadronFromDefender(Defender) -if Squadron then -self:F({SquadronName=Squadron.Name}) -local LandingMethod=self:GetSquadronLanding(Squadron.Name) -if LandingMethod==AI_A2A_DISPATCHER.Landing.AtRunway then -local DefenderSize=Defender:GetSize() -if DefenderSize==1 then -self:RemoveDefenderFromSquadron(Squadron,Defender) -end -DefenderUnit:Destroy() -self:ParkDefender(Squadron) -return -end -if DefenderUnit:GetLife()~=DefenderUnit:GetLife0()then -DefenderUnit:Destroy() -return -end -end -end -function AI_A2A_DISPATCHER:OnEventEngineShutdown(EventData) -local DefenderUnit=EventData.IniUnit -local Defender=EventData.IniGroup -local Squadron=self:GetSquadronFromDefender(Defender) -if Squadron then -self:F({SquadronName=Squadron.Name}) -local LandingMethod=self:GetSquadronLanding(Squadron.Name) -if LandingMethod==AI_A2A_DISPATCHER.Landing.AtEngineShutdown and -not DefenderUnit:InAir()then -local DefenderSize=Defender:GetSize() -if DefenderSize==1 then -self:RemoveDefenderFromSquadron(Squadron,Defender) -end -DefenderUnit:Destroy() -self:ParkDefender(Squadron) -end -end -end -function AI_A2A_DISPATCHER:SetEngageRadius(EngageRadius) -self.Detection:SetFriendliesRange(EngageRadius or 100000) -return self -end -function AI_A2A_DISPATCHER:SetDisengageRadius(DisengageRadius) -self.DisengageRadius=DisengageRadius or 300000 -return self -end -function AI_A2A_DISPATCHER:SetGciRadius(GciRadius) -self.GciRadius=GciRadius or 200000 -return self -end -function AI_A2A_DISPATCHER:SetBorderZone(BorderZone) -self.Detection:SetAcceptZones(BorderZone) -return self -end -function AI_A2A_DISPATCHER:SetTacticalDisplay(TacticalDisplay) -self.TacticalDisplay=TacticalDisplay -return self -end -function AI_A2A_DISPATCHER:SetDefaultDamageThreshold(DamageThreshold) -self.DefenderDefault.DamageThreshold=DamageThreshold -return self -end -function AI_A2A_DISPATCHER:SetDefaultCapTimeInterval(CapMinSeconds,CapMaxSeconds) -self.DefenderDefault.CapMinSeconds=CapMinSeconds -self.DefenderDefault.CapMaxSeconds=CapMaxSeconds -return self -end -function AI_A2A_DISPATCHER:SetDefaultCapLimit(CapLimit) -self.DefenderDefault.CapLimit=CapLimit -return self -end -function AI_A2A_DISPATCHER:SetIntercept(InterceptDelay) -self.DefenderDefault.InterceptDelay=InterceptDelay -local Detection=self.Detection -Detection:SetIntercept(true,InterceptDelay) -return self -end -function AI_A2A_DISPATCHER:GetAIFriendliesNearBy(DetectedItem) -local FriendliesNearBy=self.Detection:GetFriendliesDistance(DetectedItem) -return FriendliesNearBy -end -function AI_A2A_DISPATCHER:GetDefenderTasks() -return self.DefenderTasks or{} -end -function AI_A2A_DISPATCHER:GetDefenderTask(Defender) -return self.DefenderTasks[Defender] -end -function AI_A2A_DISPATCHER:GetDefenderTaskFsm(Defender) -return self:GetDefenderTask(Defender).Fsm -end -function AI_A2A_DISPATCHER:GetDefenderTaskTarget(Defender) -return self:GetDefenderTask(Defender).Target -end -function AI_A2A_DISPATCHER:GetDefenderTaskSquadronName(Defender) -return self:GetDefenderTask(Defender).SquadronName -end -function AI_A2A_DISPATCHER:ClearDefenderTask(Defender) -if Defender and Defender:IsAlive()and self.DefenderTasks[Defender]then -local Target=self.DefenderTasks[Defender].Target -local Message="Clearing ("..self.DefenderTasks[Defender].Type..") " -Message=Message..Defender:GetName() -if Target then -Message=Message..(Target and(" from "..Target.Index.." ["..Target.Set:Count().."]"))or"" -end -self:F({Target=Message}) -end -self.DefenderTasks[Defender]=nil -return self -end -function AI_A2A_DISPATCHER:ClearDefenderTaskTarget(Defender) -local DefenderTask=self:GetDefenderTask(Defender) -if Defender and Defender:IsAlive()and DefenderTask then -local Target=DefenderTask.Target -local Message="Clearing ("..DefenderTask.Type..") " -Message=Message..Defender:GetName() -if Target then -Message=Message..(Target and(" from "..Target.Index.." ["..Target.Set:Count().."]"))or"" -end -self:F({Target=Message}) -end -if Defender and DefenderTask and DefenderTask.Target then -DefenderTask.Target=nil -end -return self -end -function AI_A2A_DISPATCHER:SetDefenderTask(SquadronName,Defender,Type,Fsm,Target) -self:F({SquadronName=SquadronName,Defender=Defender:GetName(),Type=Type,Target=Target}) -self.DefenderTasks[Defender]=self.DefenderTasks[Defender]or{} -self.DefenderTasks[Defender].Type=Type -self.DefenderTasks[Defender].Fsm=Fsm -self.DefenderTasks[Defender].SquadronName=SquadronName -if Target then -self:SetDefenderTaskTarget(Defender,Target) -end -return self -end -function AI_A2A_DISPATCHER:SetDefenderTaskTarget(Defender,AttackerDetection) -local Message="("..self.DefenderTasks[Defender].Type..") " -Message=Message..Defender:GetName() -Message=Message..(AttackerDetection and(" target "..AttackerDetection.Index.." ["..AttackerDetection.Set:Count().."]"))or"" -self:F({AttackerDetection=Message}) -if AttackerDetection then -self.DefenderTasks[Defender].Target=AttackerDetection -end -return self -end -function AI_A2A_DISPATCHER:SetSquadron(SquadronName,AirbaseName,TemplatePrefixes,ResourceCount) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -local DefenderSquadron=self.DefenderSquadrons[SquadronName] -DefenderSquadron.Name=SquadronName -DefenderSquadron.Airbase=AIRBASE:FindByName(AirbaseName) -DefenderSquadron.AirbaseName=DefenderSquadron.Airbase:GetName() -if not DefenderSquadron.Airbase then -error("Cannot find airbase with name:"..AirbaseName) -end -DefenderSquadron.Spawn={} -if type(TemplatePrefixes)=="string"then -local SpawnTemplate=TemplatePrefixes -self.DefenderSpawns[SpawnTemplate]=self.DefenderSpawns[SpawnTemplate]or SPAWN:New(SpawnTemplate) -DefenderSquadron.Spawn[1]=self.DefenderSpawns[SpawnTemplate] -else -for TemplateID,SpawnTemplate in pairs(TemplatePrefixes)do -self.DefenderSpawns[SpawnTemplate]=self.DefenderSpawns[SpawnTemplate]or SPAWN:New(SpawnTemplate) -DefenderSquadron.Spawn[#DefenderSquadron.Spawn+1]=self.DefenderSpawns[SpawnTemplate] -end -end -DefenderSquadron.ResourceCount=ResourceCount -DefenderSquadron.TemplatePrefixes=TemplatePrefixes -DefenderSquadron.Captured=false -self:SetSquadronLanguage(SquadronName,"EN") -self:F({Squadron={SquadronName,AirbaseName,TemplatePrefixes,ResourceCount}}) -return self -end -function AI_A2A_DISPATCHER:GetSquadron(SquadronName) -local DefenderSquadron=self.DefenderSquadrons[SquadronName] -if not DefenderSquadron then -error("Unknown Squadron:"..SquadronName) -end -return DefenderSquadron -end -function AI_A2A_DISPATCHER:SetSquadronVisible(SquadronName) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Uncontrolled=true -DefenderSquadron.Grouping=1 -local nfreeparking=DefenderSquadron.Airbase:GetFreeParkingSpotsNumber(AIRBASE.TerminalType.FighterAircraft,true) -DefenderSquadron.ResourceCount=DefenderSquadron.ResourceCount or nfreeparking -DefenderSquadron.ResourceCount=math.min(DefenderSquadron.ResourceCount,nfreeparking) -for SpawnTemplate,_DefenderSpawn in pairs(self.DefenderSpawns)do -local DefenderSpawn=_DefenderSpawn -DefenderSpawn:InitUnControlled(true) -end -end -function AI_A2A_DISPATCHER:IsSquadronVisible(SquadronName) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -local DefenderSquadron=self:GetSquadron(SquadronName) -if DefenderSquadron then -return DefenderSquadron.Uncontrolled==true -end -return nil -end -function AI_A2A_DISPATCHER:SetSquadronCap2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{} -local DefenderSquadron=self:GetSquadron(SquadronName) -local Cap=self.DefenderSquadrons[SquadronName].Cap -Cap.Name=SquadronName -Cap.EngageMinSpeed=EngageMinSpeed -Cap.EngageMaxSpeed=EngageMaxSpeed -Cap.EngageFloorAltitude=EngageFloorAltitude -Cap.EngageCeilingAltitude=EngageCeilingAltitude -Cap.Zone=Zone -Cap.PatrolMinSpeed=PatrolMinSpeed -Cap.PatrolMaxSpeed=PatrolMaxSpeed -Cap.PatrolFloorAltitude=PatrolFloorAltitude -Cap.PatrolCeilingAltitude=PatrolCeilingAltitude -Cap.PatrolAltType=PatrolAltType -Cap.EngageAltType=EngageAltType -self:SetSquadronCapInterval(SquadronName,self.DefenderDefault.CapLimit,self.DefenderDefault.CapMinSeconds,self.DefenderDefault.CapMaxSeconds,1) -self:I({CAP={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageAltType}}) -local RecceSet=self.Detection:GetDetectionSet() -RecceSet:FilterPrefixes(DefenderSquadron.TemplatePrefixes) -RecceSet:FilterStart() -self.Detection:SetFriendlyPrefixes(DefenderSquadron.TemplatePrefixes) -return self -end -function AI_A2A_DISPATCHER:SetSquadronCap(SquadronName,Zone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType) -return self:SetSquadronCap2(SquadronName,EngageMinSpeed,EngageMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,AltType,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,AltType) -end -function AI_A2A_DISPATCHER:SetSquadronCapInterval(SquadronName,CapLimit,LowInterval,HighInterval,Probability) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{} -local DefenderSquadron=self:GetSquadron(SquadronName) -local Cap=self.DefenderSquadrons[SquadronName].Cap -if Cap then -Cap.LowInterval=LowInterval or 180 -Cap.HighInterval=HighInterval or 600 -Cap.Probability=Probability or 1 -Cap.CapLimit=CapLimit or 1 -Cap.Scheduler=Cap.Scheduler or SCHEDULER:New(self) -local Scheduler=Cap.Scheduler -local ScheduleID=Cap.ScheduleID -local Variance=(Cap.HighInterval-Cap.LowInterval)/2 -local Repeat=Cap.LowInterval+Variance -local Randomization=Variance/Repeat -local Start=math.random(1,Cap.HighInterval) -if ScheduleID then -Scheduler:Stop(ScheduleID) -end -Cap.ScheduleID=Scheduler:Schedule(self,self.SchedulerCAP,{SquadronName},Start,Repeat,Randomization) -else -error("This squadron does not exist:"..SquadronName) -end -end -function AI_A2A_DISPATCHER:GetCAPDelay(SquadronName) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{} -local DefenderSquadron=self:GetSquadron(SquadronName) -local Cap=self.DefenderSquadrons[SquadronName].Cap -if Cap then -return math.random(Cap.LowInterval,Cap.HighInterval) -else -error("This squadron does not exist:"..SquadronName) -end -end -function AI_A2A_DISPATCHER:CanCAP(SquadronName) -self:F({SquadronName=SquadronName}) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{} -local DefenderSquadron=self:GetSquadron(SquadronName) -if DefenderSquadron.Captured==false then -if(not DefenderSquadron.ResourceCount)or(DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount>0)then -local Cap=DefenderSquadron.Cap -if Cap then -local CapCount=self:CountCapAirborne(SquadronName) -self:F({CapCount=CapCount}) -if CapCount0)then -local Gci=DefenderSquadron.Gci -if Gci then -return DefenderSquadron -end -end -end -return nil -end -function AI_A2A_DISPATCHER:SetSquadronGci2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -self.DefenderSquadrons[SquadronName].Gci=self.DefenderSquadrons[SquadronName].Gci or{} -local Intercept=self.DefenderSquadrons[SquadronName].Gci -Intercept.Name=SquadronName -Intercept.EngageMinSpeed=EngageMinSpeed -Intercept.EngageMaxSpeed=EngageMaxSpeed -Intercept.EngageFloorAltitude=EngageFloorAltitude -Intercept.EngageCeilingAltitude=EngageCeilingAltitude -Intercept.EngageAltType=EngageAltType -self:I({GCI={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}}) -end -function AI_A2A_DISPATCHER:SetSquadronGci(SquadronName,EngageMinSpeed,EngageMaxSpeed) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -self.DefenderSquadrons[SquadronName].Gci=self.DefenderSquadrons[SquadronName].Gci or{} -local Intercept=self.DefenderSquadrons[SquadronName].Gci -Intercept.Name=SquadronName -Intercept.EngageMinSpeed=EngageMinSpeed -Intercept.EngageMaxSpeed=EngageMaxSpeed -self:F({GCI={SquadronName,EngageMinSpeed,EngageMaxSpeed}}) -end -function AI_A2A_DISPATCHER:SetDefaultOverhead(Overhead) -self.DefenderDefault.Overhead=Overhead -return self -end -function AI_A2A_DISPATCHER:SetSquadronOverhead(SquadronName,Overhead) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Overhead=Overhead -return self -end -function AI_A2A_DISPATCHER:SetDefaultGrouping(Grouping) -self.DefenderDefault.Grouping=Grouping -return self -end -function AI_A2A_DISPATCHER:SetSquadronGrouping(SquadronName,Grouping) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Grouping=Grouping -return self -end -function AI_A2A_DISPATCHER:SetDefaultTakeoff(Takeoff) -self.DefenderDefault.Takeoff=Takeoff -return self -end -function AI_A2A_DISPATCHER:SetSquadronTakeoff(SquadronName,Takeoff) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Takeoff=Takeoff -return self -end -function AI_A2A_DISPATCHER:GetDefaultTakeoff() -return self.DefenderDefault.Takeoff -end -function AI_A2A_DISPATCHER:GetSquadronTakeoff(SquadronName) -local DefenderSquadron=self:GetSquadron(SquadronName) -return DefenderSquadron.Takeoff or self.DefenderDefault.Takeoff -end -function AI_A2A_DISPATCHER:SetDefaultTakeoffInAir() -self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Air) -return self -end -function AI_A2A_DISPATCHER:SetSendMessages(onoff) -self.SetSendPlayerMessages=onoff -end -function AI_A2A_DISPATCHER:SetSquadronTakeoffInAir(SquadronName,TakeoffAltitude) -self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Air) -if TakeoffAltitude then -self:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude) -end -return self -end -function AI_A2A_DISPATCHER:SetDefaultTakeoffFromRunway() -self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Runway) -return self -end -function AI_A2A_DISPATCHER:SetSquadronTakeoffFromRunway(SquadronName) -self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Runway) -return self -end -function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingHot() -self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Hot) -return self -end -function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingHot(SquadronName) -self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Hot) -return self -end -function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingCold() -self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Cold) -return self -end -function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingCold(SquadronName) -self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Cold) -return self -end -function AI_A2A_DISPATCHER:SetDefaultTakeoffInAirAltitude(TakeoffAltitude) -self.DefenderDefault.TakeoffAltitude=TakeoffAltitude -return self -end -function AI_A2A_DISPATCHER:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.TakeoffAltitude=TakeoffAltitude -return self -end -function AI_A2A_DISPATCHER:SetDefaultLanding(Landing) -self.DefenderDefault.Landing=Landing -return self -end -function AI_A2A_DISPATCHER:SetSquadronLanding(SquadronName,Landing) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Landing=Landing -return self -end -function AI_A2A_DISPATCHER:GetDefaultLanding() -return self.DefenderDefault.Landing -end -function AI_A2A_DISPATCHER:GetSquadronLanding(SquadronName) -local DefenderSquadron=self:GetSquadron(SquadronName) -return DefenderSquadron.Landing or self.DefenderDefault.Landing -end -function AI_A2A_DISPATCHER:SetDefaultLandingNearAirbase() -self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.NearAirbase) -return self -end -function AI_A2A_DISPATCHER:SetSquadronLandingNearAirbase(SquadronName) -self:SetSquadronLanding(SquadronName,AI_A2A_DISPATCHER.Landing.NearAirbase) -return self -end -function AI_A2A_DISPATCHER:SetDefaultLandingAtRunway() -self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.AtRunway) -return self -end -function AI_A2A_DISPATCHER:SetSquadronLandingAtRunway(SquadronName) -self:SetSquadronLanding(SquadronName,AI_A2A_DISPATCHER.Landing.AtRunway) -return self -end -function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown() -self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.AtEngineShutdown) -return self -end -function AI_A2A_DISPATCHER:SetSquadronLandingAtEngineShutdown(SquadronName) -self:SetSquadronLanding(SquadronName,AI_A2A_DISPATCHER.Landing.AtEngineShutdown) -return self -end -function AI_A2A_DISPATCHER:SetDefaultFuelThreshold(FuelThreshold) -self.DefenderDefault.FuelThreshold=FuelThreshold -return self -end -function AI_A2A_DISPATCHER:SetSquadronFuelThreshold(SquadronName,FuelThreshold) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.FuelThreshold=FuelThreshold -return self -end -function AI_A2A_DISPATCHER:SetDefaultTanker(TankerName) -self.DefenderDefault.TankerName=TankerName -return self -end -function AI_A2A_DISPATCHER:SetSquadronTanker(SquadronName,TankerName) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.TankerName=TankerName -return self -end -function AI_A2A_DISPATCHER:SetSquadronLanguage(SquadronName,Language) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Language=Language -if DefenderSquadron.RadioQueue then -DefenderSquadron.RadioQueue:SetLanguage(Language) -end -return self -end -function AI_A2A_DISPATCHER:SetSquadronRadioFrequency(SquadronName,RadioFrequency,RadioModulation,RadioPower) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.RadioFrequency=RadioFrequency -DefenderSquadron.RadioModulation=RadioModulation or radio.modulation.AM -DefenderSquadron.RadioPower=RadioPower or 100 -if DefenderSquadron.RadioQueue then -DefenderSquadron.RadioQueue:Stop() -end -DefenderSquadron.RadioQueue=nil -DefenderSquadron.RadioQueue=RADIOSPEECH:New(DefenderSquadron.RadioFrequency,DefenderSquadron.RadioModulation) -DefenderSquadron.RadioQueue.power=DefenderSquadron.RadioPower -DefenderSquadron.RadioQueue:Start(0.5) -DefenderSquadron.RadioQueue:SetLanguage(DefenderSquadron.Language) -end -function AI_A2A_DISPATCHER:AddDefenderToSquadron(Squadron,Defender,Size) -self.Defenders=self.Defenders or{} -local DefenderName=Defender:GetName() -self.Defenders[DefenderName]=Squadron -if Squadron.ResourceCount then -Squadron.ResourceCount=Squadron.ResourceCount-Size -end -self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount}) -end -function AI_A2A_DISPATCHER:RemoveDefenderFromSquadron(Squadron,Defender) -self.Defenders=self.Defenders or{} -local DefenderName=Defender:GetName() -if Squadron.ResourceCount then -Squadron.ResourceCount=Squadron.ResourceCount+Defender:GetSize() -end -self.Defenders[DefenderName]=nil -self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount}) -end -function AI_A2A_DISPATCHER:GetSquadronFromDefender(Defender) -self.Defenders=self.Defenders or{} -if Defender~=nil then -local DefenderName=Defender:GetName() -self:F({DefenderName=DefenderName}) -return self.Defenders[DefenderName] -else -return nil -end -end -function AI_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem) -self:F({DetectedItem.ItemID}) -local DetectedSet=DetectedItem.Set -local DetectedZone=DetectedItem.Zone -if DetectedItem.IsDetected==false then -local TargetSetUnit=SET_UNIT:New() -TargetSetUnit:SetDatabase(DetectedSet) -TargetSetUnit:FilterOnce() -return TargetSetUnit -end -return nil -end -function AI_A2A_DISPATCHER:CountCapAirborne(SquadronName) -local CapCount=0 -local DefenderSquadron=self.DefenderSquadrons[SquadronName] -if DefenderSquadron then -for AIGroup,DefenderTask in pairs(self:GetDefenderTasks())do -if DefenderTask.SquadronName==SquadronName then -if DefenderTask.Type=="CAP"then -if AIGroup and AIGroup:IsAlive()then -if DefenderTask.Fsm:Is("Patrolling")or DefenderTask.Fsm:Is("Engaging")or DefenderTask.Fsm:Is("Refuelling")or DefenderTask.Fsm:Is("Started")then -CapCount=CapCount+1 -end -end -end -end -end -end -return CapCount -end -function AI_A2A_DISPATCHER:CountDefendersEngaged(AttackerDetection) -local DefenderCount=0 -local DetectedSet=AttackerDetection.Set -local DefenderTasks=self:GetDefenderTasks() -for DefenderGroup,DefenderTask in pairs(DefenderTasks)do -local Defender=DefenderGroup -local DefenderTaskTarget=DefenderTask.Target -local DefenderSquadronName=DefenderTask.SquadronName -if DefenderTaskTarget and DefenderTaskTarget.Index==AttackerDetection.Index then -local Squadron=self:GetSquadron(DefenderSquadronName) -local SquadronOverhead=Squadron.Overhead or self.DefenderDefault.Overhead -local DefenderSize=Defender:GetInitialSize() -if DefenderSize then -DefenderCount=DefenderCount+DefenderSize/SquadronOverhead -self:F("Defender Group Name: "..Defender:GetName()..", Size: "..DefenderSize) -else -DefenderCount=0 -end -end -end -self:F({DefenderCount=DefenderCount}) -return DefenderCount -end -function AI_A2A_DISPATCHER:CountDefendersToBeEngaged(AttackerDetection,DefenderCount) -local Friendlies=nil -local AttackerSet=AttackerDetection.Set -local AttackerCount=AttackerSet:Count() -local DefenderFriendlies=self:GetAIFriendliesNearBy(AttackerDetection) -for FriendlyDistance,AIFriendly in UTILS.spairs(DefenderFriendlies or{})do -if AttackerCount>DefenderCount then -local Friendly=AIFriendly:GetGroup() -if Friendly and Friendly:IsAlive()then -local DefenderTask=self:GetDefenderTask(Friendly) -if DefenderTask then -if DefenderTask.Type=="CAP"or DefenderTask.Type=="GCI"then -if DefenderTask.Target==nil then -if DefenderTask.Fsm:Is("Returning")or DefenderTask.Fsm:Is("Patrolling")then -Friendlies=Friendlies or{} -Friendlies[Friendly]=Friendly -DefenderCount=DefenderCount+Friendly:GetSize() -self:F({Friendly=Friendly:GetName(),FriendlyDistance=FriendlyDistance}) -end -end -end -end -end -else -break -end -end -return Friendlies -end -function AI_A2A_DISPATCHER:ResourceActivate(DefenderSquadron,DefendersNeeded) -local SquadronName=DefenderSquadron.Name -DefendersNeeded=DefendersNeeded or 4 -local DefenderGrouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping -DefenderGrouping=(DefenderGrouping0 then -local id=math.random(n) -local Defender=self.uncontrolled[SquadronName][id].group -Defender:StartUncontrolled() -DefenderGrouping=self.uncontrolled[SquadronName][id].grouping -self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping) -table.remove(self.uncontrolled[SquadronName],id) -return Defender,DefenderGrouping -else -return nil,0 -end -local TemplateID=math.random(1,#DefenderSquadron.Spawn) -else -local Spawn=DefenderSquadron.Spawn[math.random(1,#DefenderSquadron.Spawn)] -if DefenderGrouping then -Spawn:InitGrouping(DefenderGrouping) -else -Spawn:InitGrouping() -end -local TakeoffMethod=self:GetSquadronTakeoff(SquadronName) -local Defender=Spawn:SpawnAtAirbase(DefenderSquadron.Airbase,TakeoffMethod,DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude) -self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping) -return Defender,DefenderGrouping -end -return nil,nil -end -function AI_A2A_DISPATCHER:onafterCAP(From,Event,To,SquadronName) -self:F({SquadronName=SquadronName}) -self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} -self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{} -local DefenderSquadron=self:CanCAP(SquadronName) -if DefenderSquadron then -local Cap=DefenderSquadron.Cap -if Cap then -local DefenderCAP,DefenderGrouping=self:ResourceActivate(DefenderSquadron) -if DefenderCAP then -local AI_A2A_Fsm=AI_A2A_CAP:New2(DefenderCAP,Cap.EngageMinSpeed,Cap.EngageMaxSpeed,Cap.EngageFloorAltitude,Cap.EngageCeilingAltitude,Cap.EngageAltType,Cap.Zone,Cap.PatrolMinSpeed,Cap.PatrolMaxSpeed,Cap.PatrolFloorAltitude,Cap.PatrolCeilingAltitude,Cap.PatrolAltType) -AI_A2A_Fsm:SetDispatcher(self) -AI_A2A_Fsm:SetHomeAirbase(DefenderSquadron.Airbase) -AI_A2A_Fsm:SetFuelThreshold(DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold,60) -AI_A2A_Fsm:SetDamageThreshold(self.DefenderDefault.DamageThreshold) -AI_A2A_Fsm:SetDisengageRadius(self.DisengageRadius) -AI_A2A_Fsm:SetTanker(DefenderSquadron.TankerName or self.DefenderDefault.TankerName) -if DefenderSquadron.Racetrack or self.DefenderDefault.Racetrack then -AI_A2A_Fsm:SetRaceTrackPattern(DefenderSquadron.RacetrackLengthMin or self.DefenderDefault.RacetrackLengthMin, -DefenderSquadron.RacetrackLengthMax or self.DefenderDefault.RacetrackLengthMax, -DefenderSquadron.RacetrackHeadingMin or self.DefenderDefault.RacetrackHeadingMin, -DefenderSquadron.RacetrackHeadingMax or self.DefenderDefault.RacetrackHeadingMax, -DefenderSquadron.RacetrackDurationMin or self.DefenderDefault.RacetrackDurationMin, -DefenderSquadron.RacetrackDurationMax or self.DefenderDefault.RacetrackDurationMax, -DefenderSquadron.RacetrackCoordinates or self.DefenderDefault.RacetrackCoordinates) -end -AI_A2A_Fsm:Start() -self:SetDefenderTask(SquadronName,DefenderCAP,"CAP",AI_A2A_Fsm) -function AI_A2A_Fsm:onafterTakeoff(DefenderGroup,From,Event,To) -if DefenderGroup and DefenderGroup:IsAlive()then -self:F({"CAP Takeoff",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=AI_A2A_Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron then -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName.." Wheels up.",DefenderGroup) -end -AI_A2A_Fsm:__Patrol(2) -end -end -end -function AI_A2A_Fsm:onafterPatrolRoute(DefenderGroup,From,Event,To) -if DefenderGroup and DefenderGroup:IsAlive()then -self:F({"CAP PatrolRoute",DefenderGroup:GetName()}) -self:GetParent(self).onafterPatrolRoute(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", patrolling.",DefenderGroup) -end -Dispatcher:ClearDefenderTaskTarget(DefenderGroup) -end -end -function AI_A2A_Fsm:onafterRTB(DefenderGroup,From,Event,To) -if DefenderGroup and DefenderGroup:IsAlive()then -self:F({"CAP RTB",DefenderGroup:GetName()}) -self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName.." returning to base.",DefenderGroup) -end -Dispatcher:ClearDefenderTaskTarget(DefenderGroup) -end -end -function AI_A2A_Fsm:onafterHome(Defender,From,Event,To,Action) -if Defender and Defender:IsAlive()then -self:F({"CAP Home",Defender:GetName()}) -self:GetParent(self).onafterHome(self,Defender,From,Event,To) -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(Defender) -if Action and Action=="Destroy"then -Dispatcher:RemoveDefenderFromSquadron(Squadron,Defender) -Defender:Destroy() -end -if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2A_DISPATCHER.Landing.NearAirbase then -Dispatcher:RemoveDefenderFromSquadron(Squadron,Defender) -Defender:Destroy() -Dispatcher:ParkDefender(Squadron) -end -end -end -end -end -end -end -function AI_A2A_DISPATCHER:onafterENGAGE(From,Event,To,AttackerDetection,Defenders) -self:F("ENGAGING Detection ID="..tostring(AttackerDetection.ID)) -if Defenders then -for DefenderID,Defender in pairs(Defenders)do -local Fsm=self:GetDefenderTaskFsm(Defender) -Fsm:EngageRoute(AttackerDetection.Set) -self:SetDefenderTaskTarget(Defender,AttackerDetection) -end -end -end -function AI_A2A_DISPATCHER:onafterGCI(From,Event,To,AttackerDetection,DefendersMissing,DefenderFriendlies) -self:F("GCI Detection ID="..tostring(AttackerDetection.ID)) -self:F({From,Event,To,AttackerDetection.Index,DefendersMissing,DefenderFriendlies}) -local AttackerSet=AttackerDetection.Set -local AttackerUnit=AttackerSet:GetFirst() -if AttackerUnit and AttackerUnit:IsAlive()then -local AttackerCount=AttackerSet:Count() -local DefenderCount=0 -for DefenderID,DefenderGroup in pairs(DefenderFriendlies or{})do -local Fsm=self:GetDefenderTaskFsm(DefenderGroup) -Fsm:__EngageRoute(0.1,AttackerSet) -self:SetDefenderTaskTarget(DefenderGroup,AttackerDetection) -DefenderCount=DefenderCount+DefenderGroup:GetSize() -end -self:F({DefenderCount=DefenderCount,DefendersMissing=DefendersMissing}) -DefenderCount=DefendersMissing -local ClosestDistance=0 -local ClosestDefenderSquadronName=nil -local BreakLoop=false -while(DefenderCount>0 and not BreakLoop)do -self:F({DefenderSquadrons=self.DefenderSquadrons}) -for SquadronName,DefenderSquadron in pairs(self.DefenderSquadrons or{})do -self:F({GCI=DefenderSquadron.Gci}) -for InterceptID,Intercept in pairs(DefenderSquadron.Gci or{})do -self:F({DefenderSquadron}) -local SpawnCoord=DefenderSquadron.Airbase:GetCoordinate() -local AttackerCoord=AttackerUnit:GetCoordinate() -local InterceptCoord=AttackerDetection.InterceptCoord -self:F({InterceptCoord=InterceptCoord}) -if InterceptCoord then -local InterceptDistance=SpawnCoord:Get2DDistance(InterceptCoord) -local AirbaseDistance=SpawnCoord:Get2DDistance(AttackerCoord) -self:F({InterceptDistance=InterceptDistance,AirbaseDistance=AirbaseDistance,InterceptCoord=InterceptCoord}) -if ClosestDistance==0 or InterceptDistanceDefenderSquadron.ResourceCount then -DefendersNeeded=DefenderSquadron.ResourceCount -BreakLoop=true -end -while(DefendersNeeded>0)do -local DefenderGCI,DefenderGrouping=self:ResourceActivate(DefenderSquadron,DefendersNeeded) -DefendersNeeded=DefendersNeeded-DefenderGrouping -if DefenderGCI then -DefenderCount=DefenderCount-DefenderGrouping/DefenderOverhead -local Fsm=AI_A2A_GCI:New2(DefenderGCI,Gci.EngageMinSpeed,Gci.EngageMaxSpeed,Gci.EngageFloorAltitude,Gci.EngageCeilingAltitude,Gci.EngageAltType) -Fsm:SetDispatcher(self) -Fsm:SetHomeAirbase(DefenderSquadron.Airbase) -Fsm:SetFuelThreshold(DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold,60) -Fsm:SetDamageThreshold(self.DefenderDefault.DamageThreshold) -Fsm:SetDisengageRadius(self.DisengageRadius) -Fsm:Start() -self:SetDefenderTask(ClosestDefenderSquadronName,DefenderGCI,"GCI",Fsm,AttackerDetection) -function Fsm:onafterTakeoff(DefenderGroup,From,Event,To) -self:F({"GCI Birth",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -local DefenderTarget=Dispatcher:GetDefenderTaskTarget(DefenderGroup) -if DefenderTarget then -if Squadron.Language=="EN"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName.." wheels up.",DefenderGroup) -elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName.." колеÑ�а вверх.",DefenderGroup) -end -Fsm:EngageRoute(DefenderTarget.Set) -end -end -function Fsm:onafterEngageRoute(DefenderGroup,From,Event,To,AttackSetUnit) -self:F({"GCI Route",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron and AttackSetUnit:Count()>0 then -local FirstUnit=AttackSetUnit:GetFirst() -local Coordinate=FirstUnit:GetCoordinate() -if Squadron.Language=="EN"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", intercepting bogeys at "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup) -elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", перехват Ñ�амолетов в "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup) -elseif Squadron.Language=="DE"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", Eindringlinge abfangen bei"..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup) -end -end -self:GetParent(Fsm).onafterEngageRoute(self,DefenderGroup,From,Event,To,AttackSetUnit) -end -function Fsm:onafterEngage(DefenderGroup,From,Event,To,AttackSetUnit) -self:F({"GCI Engage",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron and AttackSetUnit:Count()>0 then -local FirstUnit=AttackSetUnit:GetFirst() -local Coordinate=FirstUnit:GetCoordinate() -if Squadron.Language=="EN"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", engaging bogeys at "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup) -elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", захватывающие Ñ�амолеты в "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup) -end -end -self:GetParent(Fsm).onafterEngage(self,DefenderGroup,From,Event,To,AttackSetUnit) -end -function Fsm:onafterRTB(DefenderGroup,From,Event,To) -self:F({"GCI RTB",DefenderGroup:GetName()}) -self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron then -if Squadron.Language=="EN"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName.." returning to base.",DefenderGroup) -elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", возвращаÑ�Ñ�ÑŒ на базу.",DefenderGroup) -end -end -Dispatcher:ClearDefenderTaskTarget(DefenderGroup) -end -function Fsm:onafterLostControl(Defender,From,Event,To) -self:F({"GCI LostControl",Defender:GetName()}) -self:GetParent(self).onafterHome(self,Defender,From,Event,To) -local Dispatcher=Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(Defender) -if Defender:IsAboveRunway()then -Dispatcher:RemoveDefenderFromSquadron(Squadron,Defender) -Defender:Destroy() -end -end -function Fsm:onafterHome(DefenderGroup,From,Event,To,Action) -self:F({"GCI Home",DefenderGroup:GetName()}) -self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron.Language=="EN"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName.." landing at base.",DefenderGroup) -elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", захватывающие Ñ�амолеты в поÑ�адка на базу.",DefenderGroup) -end -if Action and Action=="Destroy"then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -end -if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2A_DISPATCHER.Landing.NearAirbase then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -Dispatcher:ParkDefender(Squadron) -end -end -end -end -end -else -BreakLoop=true -break -end -else -break -end -end -end -end -function AI_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem) -self:F({DetectedItem.ItemID}) -local DefenderCount=self:CountDefendersEngaged(DetectedItem) -local DefenderGroups=self:CountDefendersToBeEngaged(DetectedItem,DefenderCount) -self:F({DefenderCount=DefenderCount}) -if DefenderGroups and DetectedItem.IsDetected==true then -return DefenderGroups -end -return nil -end -function AI_A2A_DISPATCHER:EvaluateGCI(DetectedItem) -self:F({DetectedItem.ItemID}) -local AttackerSet=DetectedItem.Set -local AttackerCount=AttackerSet:Count() -local DefenderCount=self:CountDefendersEngaged(DetectedItem) -local DefendersMissing=AttackerCount-DefenderCount -self:F({AttackerCount=AttackerCount,DefenderCount=DefenderCount,DefendersMissing=DefendersMissing}) -local Friendlies=self:CountDefendersToBeEngaged(DetectedItem,DefenderCount) -if DetectedItem.IsDetected==true then -return DefendersMissing,Friendlies -end -return nil,nil -end -function AI_A2A_DISPATCHER:Order(DetectedItem) -local detection=self.Detection -local ShortestDistance=999999999 -local AttackCoordinate=detection:GetDetectedItemCoordinate(DetectedItem) -if AttackCoordinate then -for DefenderSquadronName,DefenderSquadron in pairs(self.DefenderSquadrons)do -self:T({DefenderSquadron=DefenderSquadron.Name}) -local Airbase=DefenderSquadron.Airbase -local AirbaseCoordinate=Airbase:GetCoordinate() -local EvaluateDistance=AttackCoordinate:Get2DDistance(AirbaseCoordinate) -if EvaluateDistance<=ShortestDistance then -ShortestDistance=EvaluateDistance -end -end -end -return ShortestDistance -end -function AI_A2A_DISPATCHER:ShowTacticalDisplay(Detection) -local AreaMsg={} -local TaskMsg={} -local ChangeMsg={} -local TaskReport=REPORT:New() -local Report=REPORT:New("Tactical Overview:") -local DefenderGroupCount=0 -for DetectedItemID,DetectedItem in UTILS.spairs(Detection:GetDetectedItems(),function(t,a,b)return self:Order(t[a])0 then -self:F({DefendersMissing=DefendersMissing}) -self:GCI(DetectedItem,DefendersMissing,Friendlies) -end -end -end -if self.TacticalDisplay then -self:ShowTacticalDisplay(Detection) -end -return true -end -end -do -function AI_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem) -local DetectedSet=DetectedItem.Set -local PlayersNearBy=self.Detection:GetPlayersNearBy(DetectedItem) -local PlayerTypes={} -local PlayersCount=0 -if PlayersNearBy then -local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G() -for PlayerUnitName,PlayerUnitData in pairs(PlayersNearBy)do -local PlayerUnit=PlayerUnitData -local PlayerName=PlayerUnit:GetPlayerName() -if PlayerUnit:IsAirPlane()and PlayerName~=nil then -local FriendlyUnitThreatLevel=PlayerUnit:GetThreatLevel() -PlayersCount=PlayersCount+1 -local PlayerType=PlayerUnit:GetTypeName() -PlayerTypes[PlayerName]=PlayerType -if DetectedTreatLevel0 then -for PlayerName,PlayerType in pairs(PlayerTypes)do -PlayerTypesReport:Add(string.format('"%s" in %s',PlayerName,PlayerType)) -end -else -PlayerTypesReport:Add("-") -end -return PlayersCount,PlayerTypesReport -end -function AI_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem) -local DetectedSet=DetectedItem.Set -local FriendlyUnitsNearBy=self.Detection:GetFriendliesNearBy(DetectedItem) -local FriendlyTypes={} -local FriendliesCount=0 -if FriendlyUnitsNearBy then -local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G() -for FriendlyUnitName,FriendlyUnitData in pairs(FriendlyUnitsNearBy)do -local FriendlyUnit=FriendlyUnitData -if FriendlyUnit:IsAirPlane()then -local FriendlyUnitThreatLevel=FriendlyUnit:GetThreatLevel() -FriendliesCount=FriendliesCount+1 -local FriendlyType=FriendlyUnit:GetTypeName() -FriendlyTypes[FriendlyType]=FriendlyTypes[FriendlyType]and(FriendlyTypes[FriendlyType]+1)or 1 -if DetectedTreatLevel0 then -for FriendlyType,FriendlyTypeCount in pairs(FriendlyTypes)do -FriendlyTypesReport:Add(string.format("%d of %s",FriendlyTypeCount,FriendlyType)) -end -else -FriendlyTypesReport:Add("-") -end -return FriendliesCount,FriendlyTypesReport -end -function AI_A2A_DISPATCHER:SchedulerCAP(SquadronName) -self:CAP(SquadronName) -end -end -do -AI_A2A_GCICAP={ -ClassName="AI_A2A_GCICAP", -Detection=nil, -} -function AI_A2A_GCICAP:New(EWRPrefixes,TemplatePrefixes,CapPrefixes,CapLimit,GroupingRadius,EngageRadius,GciRadius,ResourceCount) -local EWRSetGroup=SET_GROUP:New() -EWRSetGroup:FilterPrefixes(EWRPrefixes) -EWRSetGroup:FilterStart() -local Detection=DETECTION_AREAS:New(EWRSetGroup,GroupingRadius or 30000) -local self=BASE:Inherit(self,AI_A2A_DISPATCHER:New(Detection)) -self:SetEngageRadius(EngageRadius) -self:SetGciRadius(GciRadius) -local EWRFirst=EWRSetGroup:GetFirst() -local EWRCoalition=EWRFirst:GetCoalition() -local AirbaseNames={} -for AirbaseID,AirbaseData in pairs(_DATABASE.AIRBASES)do -local Airbase=AirbaseData -local AirbaseName=Airbase:GetName() -if Airbase:GetCoalition()==EWRCoalition then -table.insert(AirbaseNames,AirbaseName) -end -end -self.Templates=SET_GROUP -:New() -:FilterPrefixes(TemplatePrefixes) -:FilterOnce() -self:I({Airbases=AirbaseNames}) -self:I("Defining Templates for Airbases ...") -for AirbaseID,AirbaseName in pairs(AirbaseNames)do -local Airbase=_DATABASE:FindAirbase(AirbaseName) -local AirbaseName=Airbase:GetName() -local AirbaseCoord=Airbase:GetCoordinate() -local AirbaseZone=ZONE_RADIUS:New("Airbase",AirbaseCoord:GetVec2(),3000) -local Templates=nil -self:I({Airbase=AirbaseName}) -for TemplateID,Template in pairs(self.Templates:GetSet())do -local Template=Template -local TemplateCoord=Template:GetCoordinate() -if AirbaseZone:IsVec2InZone(TemplateCoord:GetVec2())then -Templates=Templates or{} -table.insert(Templates,Template:GetName()) -self:I({Template=Template:GetName()}) -end -end -if Templates then -self:SetSquadron(AirbaseName,AirbaseName,Templates,ResourceCount) -end -end -self.CAPTemplates=SET_GROUP:New() -self.CAPTemplates:FilterPrefixes(CapPrefixes) -self.CAPTemplates:FilterOnce() -self:I("Setting up CAP ...") -for CAPID,CAPTemplate in pairs(self.CAPTemplates:GetSet())do -local CAPZone=ZONE_POLYGON:New(CAPTemplate:GetName(),CAPTemplate) -local AirbaseDistance=99999999 -local AirbaseClosest=nil -self:I({CAPZoneGroup=CAPID}) -for AirbaseID,AirbaseName in pairs(AirbaseNames)do -local Airbase=_DATABASE:FindAirbase(AirbaseName) -local AirbaseName=Airbase:GetName() -local AirbaseCoord=Airbase:GetCoordinate() -local Squadron=self.DefenderSquadrons[AirbaseName] -if Squadron then -local Distance=AirbaseCoord:Get2DDistance(CAPZone:GetCoordinate()) -self:I({AirbaseDistance=Distance}) -if Distance0)then -local Patrol=DefenderSquadron[DefenseTaskType] -if Patrol and Patrol.Patrol==true then -local PatrolCount=self:CountPatrolAirborne(SquadronName,DefenseTaskType) -self:F({PatrolCount=PatrolCount,PatrolLimit=Patrol.PatrolLimit,PatrolProbability=Patrol.Probability}) -if PatrolCount0)then -if DefenderSquadron[DefenseTaskType]and(DefenderSquadron[DefenseTaskType].Defend==true)then -return DefenderSquadron,DefenderSquadron[DefenseTaskType] -end -end -end -return nil -end -function AI_A2G_DISPATCHER:SetSquadronEngageLimit(SquadronName,EngageLimit,DefenseTaskType) -local DefenderSquadron=self:GetSquadron(SquadronName) -local Defense=DefenderSquadron[DefenseTaskType] -if Defense then -Defense.EngageLimit=EngageLimit or 1 -else -error("This squadron does not exist:"..SquadronName) -end -end -function AI_A2G_DISPATCHER:SetSquadronSead2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.SEAD=DefenderSquadron.SEAD or{} -local Sead=DefenderSquadron.SEAD -Sead.Name=SquadronName -Sead.EngageMinSpeed=EngageMinSpeed -Sead.EngageMaxSpeed=EngageMaxSpeed -Sead.EngageFloorAltitude=EngageFloorAltitude or 500 -Sead.EngageCeilingAltitude=EngageCeilingAltitude or 1000 -Sead.EngageAltType=EngageAltType -Sead.Defend=true -self:I({SEAD={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}}) -return self -end -function AI_A2G_DISPATCHER:SetSquadronSead(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude) -return self:SetSquadronSead2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,"RADIO") -end -function AI_A2G_DISPATCHER:SetSquadronSeadEngageLimit(SquadronName,EngageLimit) -self:SetSquadronEngageLimit(SquadronName,EngageLimit,"SEAD") -end -function AI_A2G_DISPATCHER:SetSquadronSeadPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.SEAD=DefenderSquadron.SEAD or{} -local SeadPatrol=DefenderSquadron.SEAD -SeadPatrol.Name=SquadronName -SeadPatrol.Zone=Zone -SeadPatrol.PatrolFloorAltitude=PatrolFloorAltitude -SeadPatrol.PatrolCeilingAltitude=PatrolCeilingAltitude -SeadPatrol.EngageFloorAltitude=EngageFloorAltitude -SeadPatrol.EngageCeilingAltitude=EngageCeilingAltitude -SeadPatrol.PatrolMinSpeed=PatrolMinSpeed -SeadPatrol.PatrolMaxSpeed=PatrolMaxSpeed -SeadPatrol.EngageMinSpeed=EngageMinSpeed -SeadPatrol.EngageMaxSpeed=EngageMaxSpeed -SeadPatrol.PatrolAltType=PatrolAltType -SeadPatrol.EngageAltType=EngageAltType -SeadPatrol.Patrol=true -self:SetSquadronPatrolInterval(SquadronName,self.DefenderDefault.PatrolLimit,self.DefenderDefault.PatrolMinSeconds,self.DefenderDefault.PatrolMaxSeconds,1,"SEAD") -self:I({SEAD={Zone:GetName(),PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}}) -end -function AI_A2G_DISPATCHER:SetSquadronSeadPatrol(SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType) -self:SetSquadronSeadPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,FloorAltitude,CeilingAltitude,AltType,EngageMinSpeed,EngageMaxSpeed,FloorAltitude,CeilingAltitude,AltType) -end -function AI_A2G_DISPATCHER:SetSquadronCas2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.CAS=DefenderSquadron.CAS or{} -local Cas=DefenderSquadron.CAS -Cas.Name=SquadronName -Cas.EngageMinSpeed=EngageMinSpeed -Cas.EngageMaxSpeed=EngageMaxSpeed -Cas.EngageFloorAltitude=EngageFloorAltitude or 500 -Cas.EngageCeilingAltitude=EngageCeilingAltitude or 1000 -Cas.EngageAltType=EngageAltType -Cas.Defend=true -self:I({CAS={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}}) -return self -end -function AI_A2G_DISPATCHER:SetSquadronCas(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude) -return self:SetSquadronCas2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,"RADIO") -end -function AI_A2G_DISPATCHER:SetSquadronCasEngageLimit(SquadronName,EngageLimit) -self:SetSquadronEngageLimit(SquadronName,EngageLimit,"CAS") -end -function AI_A2G_DISPATCHER:SetSquadronCasPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.CAS=DefenderSquadron.CAS or{} -local CasPatrol=DefenderSquadron.CAS -CasPatrol.Name=SquadronName -CasPatrol.Zone=Zone -CasPatrol.PatrolFloorAltitude=PatrolFloorAltitude -CasPatrol.PatrolCeilingAltitude=PatrolCeilingAltitude -CasPatrol.EngageFloorAltitude=EngageFloorAltitude -CasPatrol.EngageCeilingAltitude=EngageCeilingAltitude -CasPatrol.PatrolMinSpeed=PatrolMinSpeed -CasPatrol.PatrolMaxSpeed=PatrolMaxSpeed -CasPatrol.EngageMinSpeed=EngageMinSpeed -CasPatrol.EngageMaxSpeed=EngageMaxSpeed -CasPatrol.PatrolAltType=PatrolAltType -CasPatrol.EngageAltType=EngageAltType -CasPatrol.Patrol=true -self:SetSquadronPatrolInterval(SquadronName,self.DefenderDefault.PatrolLimit,self.DefenderDefault.PatrolMinSeconds,self.DefenderDefault.PatrolMaxSeconds,1,"CAS") -self:I({CAS={Zone:GetName(),PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}}) -end -function AI_A2G_DISPATCHER:SetSquadronCasPatrol(SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType) -self:SetSquadronCasPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,FloorAltitude,CeilingAltitude,AltType,EngageMinSpeed,EngageMaxSpeed,FloorAltitude,CeilingAltitude,AltType) -end -function AI_A2G_DISPATCHER:SetSquadronBai2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.BAI=DefenderSquadron.BAI or{} -local Bai=DefenderSquadron.BAI -Bai.Name=SquadronName -Bai.EngageMinSpeed=EngageMinSpeed -Bai.EngageMaxSpeed=EngageMaxSpeed -Bai.EngageFloorAltitude=EngageFloorAltitude or 500 -Bai.EngageCeilingAltitude=EngageCeilingAltitude or 1000 -Bai.EngageAltType=EngageAltType -Bai.Defend=true -self:I({BAI={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}}) -return self -end -function AI_A2G_DISPATCHER:SetSquadronBai(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude) -return self:SetSquadronBai2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,"RADIO") -end -function AI_A2G_DISPATCHER:SetSquadronBaiEngageLimit(SquadronName,EngageLimit) -self:SetSquadronEngageLimit(SquadronName,EngageLimit,"BAI") -end -function AI_A2G_DISPATCHER:SetSquadronBaiPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.BAI=DefenderSquadron.BAI or{} -local BaiPatrol=DefenderSquadron.BAI -BaiPatrol.Name=SquadronName -BaiPatrol.Zone=Zone -BaiPatrol.PatrolFloorAltitude=PatrolFloorAltitude -BaiPatrol.PatrolCeilingAltitude=PatrolCeilingAltitude -BaiPatrol.EngageFloorAltitude=EngageFloorAltitude -BaiPatrol.EngageCeilingAltitude=EngageCeilingAltitude -BaiPatrol.PatrolMinSpeed=PatrolMinSpeed -BaiPatrol.PatrolMaxSpeed=PatrolMaxSpeed -BaiPatrol.EngageMinSpeed=EngageMinSpeed -BaiPatrol.EngageMaxSpeed=EngageMaxSpeed -BaiPatrol.PatrolAltType=PatrolAltType -BaiPatrol.EngageAltType=EngageAltType -BaiPatrol.Patrol=true -self:SetSquadronPatrolInterval(SquadronName,self.DefenderDefault.PatrolLimit,self.DefenderDefault.PatrolMinSeconds,self.DefenderDefault.PatrolMaxSeconds,1,"BAI") -self:I({BAI={Zone:GetName(),PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}}) -end -function AI_A2G_DISPATCHER:SetSquadronBaiPatrol(SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType) -self:SetSquadronBaiPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,FloorAltitude,CeilingAltitude,AltType,EngageMinSpeed,EngageMaxSpeed,FloorAltitude,CeilingAltitude,AltType) -end -function AI_A2G_DISPATCHER:SetDefaultOverhead(Overhead) -self.DefenderDefault.Overhead=Overhead -return self -end -function AI_A2G_DISPATCHER:SetSquadronOverhead(SquadronName,Overhead) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Overhead=Overhead -return self -end -function AI_A2G_DISPATCHER:GetSquadronOverhead(SquadronName) -local DefenderSquadron=self:GetSquadron(SquadronName) -return DefenderSquadron.Overhead or self.DefenderDefault.Overhead -end -function AI_A2G_DISPATCHER:SetDefaultGrouping(Grouping) -self.DefenderDefault.Grouping=Grouping -return self -end -function AI_A2G_DISPATCHER:SetSquadronGrouping(SquadronName,Grouping) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Grouping=Grouping -return self -end -function AI_A2G_DISPATCHER:SetSquadronEngageProbability(SquadronName,EngageProbability) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.EngageProbability=EngageProbability -return self -end -function AI_A2G_DISPATCHER:SetDefaultTakeoff(Takeoff) -self.DefenderDefault.Takeoff=Takeoff -return self -end -function AI_A2G_DISPATCHER:SetSquadronTakeoff(SquadronName,Takeoff) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Takeoff=Takeoff -return self -end -function AI_A2G_DISPATCHER:GetDefaultTakeoff() -return self.DefenderDefault.Takeoff -end -function AI_A2G_DISPATCHER:GetSquadronTakeoff(SquadronName) -local DefenderSquadron=self:GetSquadron(SquadronName) -return DefenderSquadron.Takeoff or self.DefenderDefault.Takeoff -end -function AI_A2G_DISPATCHER:SetDefaultTakeoffInAir() -self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Air) -return self -end -function AI_A2G_DISPATCHER:SetSquadronTakeoffInAir(SquadronName,TakeoffAltitude) -self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Air) -if TakeoffAltitude then -self:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude) -end -return self -end -function AI_A2G_DISPATCHER:SetDefaultTakeoffFromRunway() -self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Runway) -return self -end -function AI_A2G_DISPATCHER:SetSquadronTakeoffFromRunway(SquadronName) -self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Runway) -return self -end -function AI_A2G_DISPATCHER:SetDefaultTakeoffFromParkingHot() -self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Hot) -return self -end -function AI_A2G_DISPATCHER:SetSquadronTakeoffFromParkingHot(SquadronName) -self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Hot) -return self -end -function AI_A2G_DISPATCHER:SetDefaultTakeoffFromParkingCold() -self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Cold) -return self -end -function AI_A2G_DISPATCHER:SetSquadronTakeoffFromParkingCold(SquadronName) -self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Cold) -return self -end -function AI_A2G_DISPATCHER:SetDefaultTakeoffInAirAltitude(TakeoffAltitude) -self.DefenderDefault.TakeoffAltitude=TakeoffAltitude -return self -end -function AI_A2G_DISPATCHER:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.TakeoffAltitude=TakeoffAltitude -return self -end -function AI_A2G_DISPATCHER:SetDefaultLanding(Landing) -self.DefenderDefault.Landing=Landing -return self -end -function AI_A2G_DISPATCHER:SetSquadronLanding(SquadronName,Landing) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.Landing=Landing -return self -end -function AI_A2G_DISPATCHER:GetDefaultLanding() -return self.DefenderDefault.Landing -end -function AI_A2G_DISPATCHER:GetSquadronLanding(SquadronName) -local DefenderSquadron=self:GetSquadron(SquadronName) -return DefenderSquadron.Landing or self.DefenderDefault.Landing -end -function AI_A2G_DISPATCHER:SetDefaultLandingNearAirbase() -self:SetDefaultLanding(AI_A2G_DISPATCHER.Landing.NearAirbase) -return self -end -function AI_A2G_DISPATCHER:SetSquadronLandingNearAirbase(SquadronName) -self:SetSquadronLanding(SquadronName,AI_A2G_DISPATCHER.Landing.NearAirbase) -return self -end -function AI_A2G_DISPATCHER:SetDefaultLandingAtRunway() -self:SetDefaultLanding(AI_A2G_DISPATCHER.Landing.AtRunway) -return self -end -function AI_A2G_DISPATCHER:SetSquadronLandingAtRunway(SquadronName) -self:SetSquadronLanding(SquadronName,AI_A2G_DISPATCHER.Landing.AtRunway) -return self -end -function AI_A2G_DISPATCHER:SetDefaultLandingAtEngineShutdown() -self:SetDefaultLanding(AI_A2G_DISPATCHER.Landing.AtEngineShutdown) -return self -end -function AI_A2G_DISPATCHER:SetSquadronLandingAtEngineShutdown(SquadronName) -self:SetSquadronLanding(SquadronName,AI_A2G_DISPATCHER.Landing.AtEngineShutdown) -return self -end -function AI_A2G_DISPATCHER:SetDefaultFuelThreshold(FuelThreshold) -self.DefenderDefault.FuelThreshold=FuelThreshold -return self -end -function AI_A2G_DISPATCHER:SetSquadronFuelThreshold(SquadronName,FuelThreshold) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.FuelThreshold=FuelThreshold -return self -end -function AI_A2G_DISPATCHER:SetDefaultTanker(TankerName) -self.DefenderDefault.TankerName=TankerName -return self -end -function AI_A2G_DISPATCHER:SetSquadronTanker(SquadronName,TankerName) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.TankerName=TankerName -return self -end -function AI_A2G_DISPATCHER:SetSquadronRadioFrequency(SquadronName,RadioFrequency,RadioModulation,RadioPower) -local DefenderSquadron=self:GetSquadron(SquadronName) -DefenderSquadron.RadioFrequency=RadioFrequency -DefenderSquadron.RadioModulation=RadioModulation or radio.modulation.AM -DefenderSquadron.RadioPower=RadioPower or 100 -if DefenderSquadron.RadioQueue then -DefenderSquadron.RadioQueue:Stop() -end -DefenderSquadron.RadioQueue=nil -DefenderSquadron.RadioQueue=RADIOSPEECH:New(DefenderSquadron.RadioFrequency,DefenderSquadron.RadioModulation) -DefenderSquadron.RadioQueue.power=DefenderSquadron.RadioPower -DefenderSquadron.RadioQueue:Start(0.5) -DefenderSquadron.RadioQueue:SetLanguage(DefenderSquadron.Language) -end -function AI_A2G_DISPATCHER:AddDefenderToSquadron(Squadron,Defender,Size) -self.Defenders=self.Defenders or{} -local DefenderName=Defender:GetName() -self.Defenders[DefenderName]=Squadron -if Squadron.ResourceCount then -Squadron.ResourceCount=Squadron.ResourceCount-Size -end -self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount}) -end -function AI_A2G_DISPATCHER:RemoveDefenderFromSquadron(Squadron,Defender) -self.Defenders=self.Defenders or{} -local DefenderName=Defender:GetName() -if Squadron.ResourceCount then -Squadron.ResourceCount=Squadron.ResourceCount+Defender:GetSize() -end -self.Defenders[DefenderName]=nil -self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount}) -end -function AI_A2G_DISPATCHER:GetSquadronFromDefender(Defender) -self.Defenders=self.Defenders or{} -local DefenderName=Defender:GetName() -self:F({DefenderName=DefenderName}) -return self.Defenders[DefenderName] -end -function AI_A2G_DISPATCHER:CountPatrolAirborne(SquadronName,DefenseTaskType) -local PatrolCount=0 -local DefenderSquadron=self.DefenderSquadrons[SquadronName] -if DefenderSquadron then -for AIGroup,DefenderTask in pairs(self:GetDefenderTasks())do -if DefenderTask.SquadronName==SquadronName then -if DefenderTask.Type==DefenseTaskType then -if AIGroup:IsAlive()then -if DefenderTask.Fsm:Is("Patrolling")or DefenderTask.Fsm:Is("Engaging")or DefenderTask.Fsm:Is("Refuelling") -or DefenderTask.Fsm:Is("Started")then -PatrolCount=PatrolCount+1 -end -end -end -end -end -end -return PatrolCount -end -function AI_A2G_DISPATCHER:CountDefendersEngaged(AttackerDetection,AttackerCount) -local DefendersEngaged=0 -local DefendersTotal=0 -local AttackerSet=AttackerDetection.Set -local DefendersMissing=AttackerCount -local DefenderTasks=self:GetDefenderTasks() -for DefenderGroup,DefenderTask in pairs(DefenderTasks)do -local Defender=DefenderGroup -local DefenderTaskTarget=DefenderTask.Target -local DefenderSquadronName=DefenderTask.SquadronName -local DefenderSize=DefenderTask.Size -if DefenderTask.Target then -self:F("Defender Group Name: "..Defender:GetName()..", Size: "..DefenderSize) -DefendersTotal=DefendersTotal+DefenderSize -if DefenderTaskTarget and DefenderTaskTarget.Index==AttackerDetection.Index then -local SquadronOverhead=self:GetSquadronOverhead(DefenderSquadronName) -self:F({SquadronOverhead=SquadronOverhead}) -if DefenderSize then -DefendersEngaged=DefendersEngaged+DefenderSize -DefendersMissing=DefendersMissing-DefenderSize/SquadronOverhead -self:F("Defender Group Name: "..Defender:GetName()..", Size: "..DefenderSize) -else -DefendersEngaged=0 -end -end -end -end -for QueueID,QueueItem in pairs(self.DefenseQueue)do -local QueueItem=QueueItem -if QueueItem.AttackerDetection and QueueItem.AttackerDetection.ItemID==AttackerDetection.ItemID then -DefendersMissing=DefendersMissing-QueueItem.DefendersNeeded/QueueItem.DefenderSquadron.Overhead -self:F({QueueItemName=QueueItem.Defense,QueueItem_ItemID=QueueItem.AttackerDetection.ItemID,DetectedItem=AttackerDetection.ItemID,DefendersMissing=DefendersMissing}) -end -end -self:F({DefenderCount=DefendersEngaged}) -return DefendersTotal,DefendersEngaged,DefendersMissing -end -function AI_A2G_DISPATCHER:CountDefenders(AttackerDetection,DefenderCount,DefenderTaskType) -local Friendlies=nil -local AttackerSet=AttackerDetection.Set -local AttackerCount=AttackerSet:Count() -local DefenderFriendlies=self:GetDefenderFriendliesNearBy(AttackerDetection) -for FriendlyDistance,DefenderFriendlyUnit in UTILS.spairs(DefenderFriendlies or{})do -if AttackerCount>DefenderCount then -local FriendlyGroup=DefenderFriendlyUnit:GetGroup() -if FriendlyGroup and FriendlyGroup:IsAlive()then -local DefenderTask=self:GetDefenderTask(FriendlyGroup) -if DefenderTask then -if DefenderTaskType==DefenderTask.Type then -if DefenderTask.Target==nil then -if DefenderTask.Fsm:Is("Returning") -or DefenderTask.Fsm:Is("Patrolling")then -Friendlies=Friendlies or{} -Friendlies[FriendlyGroup]=FriendlyGroup -DefenderCount=DefenderCount+FriendlyGroup:GetSize() -self:F({Friendly=FriendlyGroup:GetName(),FriendlyDistance=FriendlyDistance}) -end -end -end -end -end -else -break -end -end -return Friendlies -end -function AI_A2G_DISPATCHER:ResourceActivate(DefenderSquadron,DefendersNeeded) -local SquadronName=DefenderSquadron.Name -DefendersNeeded=DefendersNeeded or 4 -local DefenderGrouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping -DefenderGrouping=(DefenderGroupingDefenderGrouping then -break -end -end -if DefenderPatrolTemplate then -local TakeoffMethod=self:GetSquadronTakeoff(SquadronName) -local SpawnGroup=GROUP:Register(DefenderName) -DefenderPatrolTemplate.lateActivation=nil -DefenderPatrolTemplate.uncontrolled=nil -local Takeoff=self:GetSquadronTakeoff(SquadronName) -DefenderPatrolTemplate.route.points[1].type=GROUPTEMPLATE.Takeoff[Takeoff][1] -DefenderPatrolTemplate.route.points[1].action=GROUPTEMPLATE.Takeoff[Takeoff][2] -local Defender=_DATABASE:Spawn(DefenderPatrolTemplate) -self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping) -Defender:Activate() -return Defender,DefenderGrouping -end -else -local Spawn=DefenderSquadron.Spawn[math.random(1,#DefenderSquadron.Spawn)] -if DefenderGrouping then -Spawn:InitGrouping(DefenderGrouping) -else -Spawn:InitGrouping() -end -local TakeoffMethod=self:GetSquadronTakeoff(SquadronName) -local Defender=Spawn:SpawnAtAirbase(DefenderSquadron.Airbase,TakeoffMethod,DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude) -self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping) -return Defender,DefenderGrouping -end -return nil,nil -end -function AI_A2G_DISPATCHER:onafterPatrol(From,Event,To,SquadronName,DefenseTaskType) -local DefenderSquadron,Patrol=self:CanPatrol(SquadronName,DefenseTaskType) -if DefenderSquadron then -local DefendersNeeded -local DefendersGrouping=(DefenderSquadron.Grouping or self.DefenderDefault.Grouping) -if DefenderSquadron.ResourceCount==nil then -DefendersNeeded=DefendersGrouping -else -if DefenderSquadron.ResourceCount>=DefendersGrouping then -DefendersNeeded=DefendersGrouping -else -DefendersNeeded=DefenderSquadron.ResourceCount -end -end -if Patrol then -self:ResourceQueue(true,DefenderSquadron,DefendersNeeded,Patrol,DefenseTaskType,nil,SquadronName) -end -end -end -function AI_A2G_DISPATCHER:ResourceQueue(Patrol,DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,AttackerDetection,SquadronName) -self:F({DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,AttackerDetection,SquadronName}) -local DefenseQueueItem={} -DefenseQueueItem.Patrol=Patrol -DefenseQueueItem.DefenderSquadron=DefenderSquadron -DefenseQueueItem.DefendersNeeded=DefendersNeeded -DefenseQueueItem.Defense=Defense -DefenseQueueItem.DefenseTaskType=DefenseTaskType -DefenseQueueItem.AttackerDetection=AttackerDetection -DefenseQueueItem.SquadronName=SquadronName -table.insert(self.DefenseQueue,DefenseQueueItem) -self:F({QueueItems=#self.DefenseQueue}) -end -function AI_A2G_DISPATCHER:ResourceTakeoff() -for DefenseQueueID,DefenseQueueItem in pairs(self.DefenseQueue)do -self:F({DefenseQueueID}) -end -for SquadronName,Squadron in pairs(self.DefenderSquadrons)do -if#self.DefenseQueue>0 then -self:F({SquadronName,Squadron.Name,Squadron.TakeoffTime,Squadron.TakeoffInterval,timer.getTime()}) -local DefenseQueueItem=self.DefenseQueue[1] -self:F({DefenderSquadron=DefenseQueueItem.DefenderSquadron}) -if DefenseQueueItem.SquadronName==SquadronName then -if Squadron.TakeoffTime+Squadron.TakeoffInterval0 then -local FirstUnit=AttackSetUnit:GetFirst() -local Coordinate=FirstUnit:GetCoordinate() -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", moving on to ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup) -end -end -end -function AI_A2G_Fsm:OnAfterEngage(DefenderGroup,From,Event,To,AttackSetUnit) -self:F({"Engage Route",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=AI_A2G_Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -local FirstUnit=AttackSetUnit:GetFirst() -if FirstUnit then -local Coordinate=FirstUnit:GetCoordinate() -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", engaging ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup) -end -end -end -function AI_A2G_Fsm:onafterRTB(DefenderGroup,From,Event,To) -self:F({"RTB",DefenderGroup:GetName()}) -self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", returning to base.",DefenderGroup) -end -Dispatcher:ClearDefenderTaskTarget(DefenderGroup) -end -function AI_A2G_Fsm:onafterLostControl(DefenderGroup,From,Event,To) -self:F({"LostControl",DefenderGroup:GetName()}) -self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=AI_A2G_Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", lost control.") -end -if DefenderGroup:IsAboveRunway()then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -end -end -function AI_A2G_Fsm:onafterHome(DefenderGroup,From,Event,To,Action) -self:F({"Home",DefenderGroup:GetName()}) -self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", landing at base.",DefenderGroup) -end -if Action and Action=="Destroy"then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -end -if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2G_DISPATCHER.Landing.NearAirbase then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -Dispatcher:ResourcePark(Squadron,DefenderGroup) -end -end -end -end -function AI_A2G_DISPATCHER:ResourceEngage(DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,AttackerDetection,SquadronName) -self:F({DefenderSquadron=DefenderSquadron}) -self:F({DefendersNeeded=DefendersNeeded}) -self:F({Defense=Defense}) -self:F({DefenseTaskType=DefenseTaskType}) -self:F({AttackerDetection=AttackerDetection}) -self:F({SquadronName=SquadronName}) -local DefenderGroup,DefenderGrouping=self:ResourceActivate(DefenderSquadron,DefendersNeeded) -if DefenderGroup then -local AI_A2G_ENGAGE={SEAD=AI_A2G_SEAD,BAI=AI_A2G_BAI,CAS=AI_A2G_CAS} -local AI_A2G_Fsm=AI_A2G_ENGAGE[DefenseTaskType]:New(DefenderGroup,Defense.EngageMinSpeed,Defense.EngageMaxSpeed,Defense.EngageFloorAltitude,Defense.EngageCeilingAltitude,Defense.EngageAltType) -AI_A2G_Fsm:SetDispatcher(self) -AI_A2G_Fsm:SetHomeAirbase(DefenderSquadron.Airbase) -AI_A2G_Fsm:SetFuelThreshold(DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold,60) -AI_A2G_Fsm:SetDamageThreshold(self.DefenderDefault.DamageThreshold) -AI_A2G_Fsm:SetDisengageRadius(self.DisengageRadius) -AI_A2G_Fsm:Start() -self:SetDefenderTask(SquadronName,DefenderGroup,DefenseTaskType,AI_A2G_Fsm,AttackerDetection,DefenderGrouping) -function AI_A2G_Fsm:onafterTakeoff(DefenderGroup,From,Event,To) -self:F({"Defender Birth",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=AI_A2G_Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -local DefenderTarget=Dispatcher:GetDefenderTaskTarget(DefenderGroup) -self:F({DefenderTarget=DefenderTarget}) -if DefenderTarget then -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", wheels up.",DefenderGroup) -end -AI_A2G_Fsm:EngageRoute(DefenderTarget.Set) -end -end -function AI_A2G_Fsm:onafterEngageRoute(DefenderGroup,From,Event,To,AttackSetUnit) -self:F({"Engage Route",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=AI_A2G_Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if Squadron then -local FirstUnit=AttackSetUnit:GetFirst() -local Coordinate=FirstUnit:GetCoordinate() -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", on route to ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup) -end -end -self:GetParent(self).onafterEngageRoute(self,DefenderGroup,From,Event,To,AttackSetUnit) -end -function AI_A2G_Fsm:OnAfterEngage(DefenderGroup,From,Event,To,AttackSetUnit) -self:F({"Engage Route",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=AI_A2G_Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -local FirstUnit=AttackSetUnit:GetFirst() -if FirstUnit then -local Coordinate=FirstUnit:GetCoordinate() -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", engaging ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup) -end -end -end -function AI_A2G_Fsm:onafterRTB(DefenderGroup,From,Event,To) -self:F({"Defender RTB",DefenderGroup:GetName()}) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", returning to base.",DefenderGroup) -end -self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To) -Dispatcher:ClearDefenderTaskTarget(DefenderGroup) -end -function AI_A2G_Fsm:onafterLostControl(DefenderGroup,From,Event,To) -self:F({"Defender LostControl",DefenderGroup:GetName()}) -self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=AI_A2G_Fsm:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,"Squadron "..Squadron.Name..", "..DefenderName.." lost control.") -end -if DefenderGroup:IsAboveRunway()then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -end -end -function AI_A2G_Fsm:onafterHome(DefenderGroup,From,Event,To,Action) -self:F({"Defender Home",DefenderGroup:GetName()}) -self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To) -local DefenderName=DefenderGroup:GetCallsign() -local Dispatcher=self:GetDispatcher() -local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup) -if self.SetSendPlayerMessages then -Dispatcher:MessageToPlayers(Squadron,DefenderName..", landing at base.",DefenderGroup) -end -if Action and Action=="Destroy"then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -end -if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2G_DISPATCHER.Landing.NearAirbase then -Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup) -DefenderGroup:Destroy() -Dispatcher:ResourcePark(Squadron,DefenderGroup) -end -end -end -end -function AI_A2G_DISPATCHER:onafterEngage(From,Event,To,AttackerDetection,Defenders) -if Defenders then -for DefenderID,Defender in pairs(Defenders or{})do -local Fsm=self:GetDefenderTaskFsm(Defender) -Fsm:Engage(AttackerDetection.Set) -self:SetDefenderTaskTarget(Defender,AttackerDetection) -end -end -end -function AI_A2G_DISPATCHER:HasDefenseLine(DefenseCoordinate,DetectedItem) -local AttackCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem) -local EvaluateDistance=AttackCoordinate:Get2DDistance(DefenseCoordinate) -local c1=DefenseCoordinate -local c2=AttackCoordinate -local a=c1.z-c2.z -local b=c2.x-c1.x -local c=c1.x*c2.z-c2.x*c1.z -local ok=true -for AttackItemID,CheckAttackItem in pairs(self.Detection:GetDetectedItems())do -if AttackItemID~=DetectedItem.ID then -local CheckAttackCoordinate=self.Detection:GetDetectedItemCoordinate(CheckAttackItem) -local x=CheckAttackCoordinate.x -local y=CheckAttackCoordinate.z -local r=5000 -local IntersectDistance=(math.abs(a*x+b*y+c))/math.sqrt(a*a+b*b) -self:F({IntersectDistance=IntersectDistance,x=x,y=y}) -local IntersectAttackDistance=CheckAttackCoordinate:Get2DDistance(DefenseCoordinate) -self:F({IntersectAttackDistance=IntersectAttackDistance,EvaluateDistance=EvaluateDistance}) -if IntersectDistance0 and not BreakLoop)do -self:F({DefenderSquadrons=self.DefenderSquadrons}) -for SquadronName,DefenderSquadron in UTILS.rpairs(self.DefenderSquadrons or{})do -if DefenderSquadron[DefenseTaskType]then -local AirbaseCoordinate=DefenderSquadron.Airbase:GetCoordinate() -local AttackerCoord=AttackerUnit:GetCoordinate() -local InterceptCoord=DetectedItem.InterceptCoord -self:F({InterceptCoord=InterceptCoord}) -if InterceptCoord then -local InterceptDistance=AirbaseCoordinate:Get2DDistance(InterceptCoord) -local AirbaseDistance=AirbaseCoordinate:Get2DDistance(AttackerCoord) -self:F({InterceptDistance=InterceptDistance,AirbaseDistance=AirbaseDistance,InterceptCoord=InterceptCoord}) -if AirbaseDistance<=self.DefenseRadius then -local HasDefenseLine=self:HasDefenseLine(AirbaseCoordinate,DetectedItem) -if HasDefenseLine==true then -local EngageProbability=(DefenderSquadron.EngageProbability or 1) -local Probability=math.random() -if Probability=DefendersLimit then -DefendersNeeded=0 -BreakLoop=true -else -if DefendersTotal+DefendersNeeded>DefendersLimit then -DefendersNeeded=DefendersLimit-DefendersTotal -end -end -end -if DefenderSquadron.ResourceCount and DefendersNeeded>DefenderSquadron.ResourceCount then -DefendersNeeded=DefenderSquadron.ResourceCount -BreakLoop=true -end -while(DefendersNeeded>0)do -self:ResourceQueue(false,DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,DetectedItem,EngageSquadronName) -DefendersNeeded=DefendersNeeded-DefenderGrouping -DefenderCount=DefenderCount-DefenderGrouping/DefenderOverhead -end -else -BreakLoop=true -break -end -else -break -end -end -end -end -function AI_A2G_DISPATCHER:Evaluate_SEAD(DetectedItem) -self:F({DetectedItem.ItemID}) -local AttackerSet=DetectedItem.Set -local AttackerCount=AttackerSet:HasSEAD() -if(AttackerCount>0)then -local DefendersTotal,DefendersEngaged,DefendersMissing=self:CountDefendersEngaged(DetectedItem,AttackerCount) -self:F({AttackerCount=AttackerCount,DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing}) -local DefenderGroups=self:CountDefenders(DetectedItem,DefendersEngaged,"SEAD") -if DetectedItem.IsDetected==true then -return DefendersTotal,DefendersEngaged,DefendersMissing,DefenderGroups -end -end -return 0,0,0 -end -function AI_A2G_DISPATCHER:Evaluate_CAS(DetectedItem) -self:F({DetectedItem.ItemID}) -local AttackerSet=DetectedItem.Set -local AttackerCount=AttackerSet:Count() -local AttackerRadarCount=AttackerSet:HasSEAD() -local IsFriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -local IsCas=(AttackerRadarCount==0)and(IsFriendliesNearBy==true) -if IsCas==true then -local DefendersTotal,DefendersEngaged,DefendersMissing=self:CountDefendersEngaged(DetectedItem,AttackerCount) -self:F({AttackerCount=AttackerCount,DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing}) -local DefenderGroups=self:CountDefenders(DetectedItem,DefendersEngaged,"CAS") -if DetectedItem.IsDetected==true then -return DefendersTotal,DefendersEngaged,DefendersMissing,DefenderGroups -end -end -return 0,0,0 -end -function AI_A2G_DISPATCHER:Evaluate_BAI(DetectedItem) -self:F({DetectedItem.ItemID}) -local AttackerSet=DetectedItem.Set -local AttackerCount=AttackerSet:Count() -local AttackerRadarCount=AttackerSet:HasSEAD() -local IsFriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -local IsBai=(AttackerRadarCount==0)and(IsFriendliesNearBy==false) -if IsBai==true then -local DefendersTotal,DefendersEngaged,DefendersMissing=self:CountDefendersEngaged(DetectedItem,AttackerCount) -self:F({AttackerCount=AttackerCount,DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing}) -local DefenderGroups=self:CountDefenders(DetectedItem,DefendersEngaged,"BAI") -if DetectedItem.IsDetected==true then -return DefendersTotal,DefendersEngaged,DefendersMissing,DefenderGroups -end -end -return 0,0,0 -end -function AI_A2G_DISPATCHER:Keys(DetectedItem) -self:F({DetectedItem=DetectedItem}) -local AttackCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem) -local ShortestDistance=999999999 -for DefenseCoordinateName,DefenseCoordinate in pairs(self.DefenseCoordinates)do -local DefenseCoordinate=DefenseCoordinate -local EvaluateDistance=AttackCoordinate:Get2DDistance(DefenseCoordinate) -if EvaluateDistance<=ShortestDistance then -ShortestDistance=EvaluateDistance -end -end -return ShortestDistance -end -function AI_A2G_DISPATCHER:Order(DetectedItem) -local AttackCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem) -local ShortestDistance=999999999 -for DefenseCoordinateName,DefenseCoordinate in pairs(self.DefenseCoordinates)do -local DefenseCoordinate=DefenseCoordinate -local EvaluateDistance=AttackCoordinate:Get2DDistance(DefenseCoordinate) -if EvaluateDistance<=ShortestDistance then -ShortestDistance=EvaluateDistance -end -end -return ShortestDistance -end -function AI_A2G_DISPATCHER:ShowTacticalDisplay(Detection) -local AreaMsg={} -local TaskMsg={} -local ChangeMsg={} -local TaskReport=REPORT:New() -local DefenseTotal=0 -local Report=REPORT:New("\nTactical Overview") -local DefenderGroupCount=0 -local DefendersTotal=0 -for DetectedItemID,DetectedItem in UTILS.spairs(Detection:GetDetectedItems(),function(t,a,b)return self:Order(t[a])0 then -self:F({DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing}) -self:Defend(DetectedItem,DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies,"SEAD") -end -end -do -local DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies=self:Evaluate_CAS(DetectedItem) -if DefendersMissing>0 then -self:F({DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing}) -self:Defend(DetectedItem,DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies,"CAS") -end -end -do -local DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies=self:Evaluate_BAI(DetectedItem) -if DefendersMissing>0 then -self:F({DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing}) -self:Defend(DetectedItem,DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies,"BAI") -end -end -end -for Defender,DefenderTask in pairs(self:GetDefenderTasks())do -local Defender=Defender -if DefenderTask.Target and DefenderTask.Target.Index==DetectedItem.Index then -DefenseTotal=DefenseTotal+1 -end -end -for DefenseQueueID,DefenseQueueItem in pairs(self.DefenseQueue)do -local DefenseQueueItem=DefenseQueueItem -if DefenseQueueItem.AttackerDetection and DefenseQueueItem.AttackerDetection.Index and DefenseQueueItem.AttackerDetection.Index==DetectedItem.Index then -DefenseTotal=DefenseTotal+1 -end -end -if self.TacticalDisplay then -local ThreatLevel=DetectedItem.Set:CalculateThreatLevelA2G() -Report:Add(string.format(" - %1s%s ( %4s ): ( #%d - %4s ) %s",(DetectedItem.IsDetected==true)and"!"or" ",DetectedItem.ItemID,DetectedItem.Index,DetectedItem.Set:Count(),DetectedItem.Type or" --- ",string.rep("■",ThreatLevel))) -for Defender,DefenderTask in pairs(self:GetDefenderTasks())do -local Defender=Defender -if DefenderTask.Target and DefenderTask.Target.Index==DetectedItem.Index then -if Defender:IsAlive()then -DefenderGroupCount=DefenderGroupCount+1 -local Fuel=Defender:GetFuelMin()*100 -local Damage=Defender:GetLife()/Defender:GetLife0()*100 -Report:Add(string.format(" - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", -Defender:GetName(), -DefenderTask.Type, -DefenderTask.Fsm:GetState(), -Defender:GetSize(), -Fuel, -Damage, -Defender:HasTask()==true and"Executing"or"Idle")) -end -end -end -end -end -end -if self.TacticalDisplay then -Report:Add("\n - No Targets:") -local TaskCount=0 -for Defender,DefenderTask in pairs(self:GetDefenderTasks())do -TaskCount=TaskCount+1 -local Defender=Defender -if not DefenderTask.Target then -if Defender:IsAlive()then -local DefenderHasTask=Defender:HasTask() -local Fuel=Defender:GetFuelMin()*100 -local Damage=Defender:GetLife()/Defender:GetLife0()*100 -DefenderGroupCount=DefenderGroupCount+1 -Report:Add(string.format(" - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", -Defender:GetName(), -DefenderTask.Type, -DefenderTask.Fsm:GetState(), -Defender:GetSize(), -Fuel, -Damage, -Defender:HasTask()==true and"Executing"or"Idle")) -end -end -end -Report:Add(string.format("\n - %d Tasks - %d Defender Groups",TaskCount,DefenderGroupCount)) -Report:Add(string.format("\n - %d Queued Aircraft Launches",#self.DefenseQueue)) -for DefenseQueueID,DefenseQueueItem in pairs(self.DefenseQueue)do -local DefenseQueueItem=DefenseQueueItem -Report:Add(string.format(" - %s - %s",DefenseQueueItem.SquadronName,DefenseQueueItem.DefenderSquadron.TakeoffTime,DefenseQueueItem.DefenderSquadron.TakeoffInterval)) -end -Report:Add(string.format("\n - Squadron Resources: ",#self.DefenseQueue)) -for DefenderSquadronName,DefenderSquadron in pairs(self.DefenderSquadrons)do -Report:Add(string.format(" - %s - %s",DefenderSquadronName,DefenderSquadron.ResourceCount and tostring(DefenderSquadron.ResourceCount)or"n/a")) -end -self:F(Report:Text("\n")) -trigger.action.outText(Report:Text("\n"),25) -end -return true -end -end -do -function AI_A2G_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem) -local DetectedSet=DetectedItem.Set -local PlayersNearBy=self.Detection:GetPlayersNearBy(DetectedItem) -local PlayerTypes={} -local PlayersCount=0 -if PlayersNearBy then -local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G() -for PlayerUnitName,PlayerUnitData in pairs(PlayersNearBy)do -local PlayerUnit=PlayerUnitData -local PlayerName=PlayerUnit:GetPlayerName() -if PlayerUnit:IsAirPlane()and PlayerName~=nil then -local FriendlyUnitThreatLevel=PlayerUnit:GetThreatLevel() -PlayersCount=PlayersCount+1 -local PlayerType=PlayerUnit:GetTypeName() -PlayerTypes[PlayerName]=PlayerType -if DetectedTreatLevel0 then -for PlayerName,PlayerType in pairs(PlayerTypes)do -PlayerTypesReport:Add(string.format('"%s" in %s',PlayerName,PlayerType)) -end -else -PlayerTypesReport:Add("-") -end -return PlayersCount,PlayerTypesReport -end -function AI_A2G_DISPATCHER:GetFriendliesNearBy(DetectedItem) -local DetectedSet=DetectedItem.Set -local FriendlyUnitsNearBy=self.Detection:GetFriendliesNearBy(DetectedItem) -local FriendlyTypes={} -local FriendliesCount=0 -if FriendlyUnitsNearBy then -local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G() -for FriendlyUnitName,FriendlyUnitData in pairs(FriendlyUnitsNearBy)do -local FriendlyUnit=FriendlyUnitData -if FriendlyUnit:IsAirPlane()then -local FriendlyUnitThreatLevel=FriendlyUnit:GetThreatLevel() -FriendliesCount=FriendliesCount+1 -local FriendlyType=FriendlyUnit:GetTypeName() -FriendlyTypes[FriendlyType]=FriendlyTypes[FriendlyType]and(FriendlyTypes[FriendlyType]+1)or 1 -if DetectedTreatLevel0 then -for FriendlyType,FriendlyTypeCount in pairs(FriendlyTypes)do -FriendlyTypesReport:Add(string.format("%d of %s",FriendlyTypeCount,FriendlyType)) -end -else -FriendlyTypesReport:Add("-") -end -return FriendliesCount,FriendlyTypesReport -end -function AI_A2G_DISPATCHER:SchedulerPatrol(SquadronName) -local PatrolTaskTypes={"SEAD","CAS","BAI"} -local PatrolTaskType=PatrolTaskTypes[math.random(1,3)] -self:Patrol(SquadronName,PatrolTaskType) -end -function AI_A2G_DISPATCHER:SetSendMessages(onoff) -self.SetSendPlayerMessages=onoff -end -end -AI_PATROL_ZONE={ -ClassName="AI_PATROL_ZONE", -} -function AI_PATROL_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType) -local self=BASE:Inherit(self,FSM_CONTROLLABLE:New()) -self.PatrolZone=PatrolZone -self.PatrolFloorAltitude=PatrolFloorAltitude -self.PatrolCeilingAltitude=PatrolCeilingAltitude -self.PatrolMinSpeed=PatrolMinSpeed -self.PatrolMaxSpeed=PatrolMaxSpeed -self.PatrolAltType=PatrolAltType or"BARO" -self:SetRefreshTimeInterval(30) -self.CheckStatus=true -self:ManageFuel(.2,60) -self:ManageDamage(1) -self.DetectedUnits={} -self:SetStartState("None") -self:AddTransition("*","Stop","Stopped") -self:AddTransition("None","Start","Patrolling") -self:AddTransition("Patrolling","Route","Patrolling") -self:AddTransition("*","Status","*") -self:AddTransition("*","Detect","*") -self:AddTransition("*","Detected","*") -self:AddTransition("*","RTB","Returning") -self:AddTransition("*","Reset","Patrolling") -self:AddTransition("*","Eject","*") -self:AddTransition("*","Crash","Crashed") -self:AddTransition("*","PilotDead","*") -return self -end -function AI_PATROL_ZONE:SetSpeed(PatrolMinSpeed,PatrolMaxSpeed) -self:F2({PatrolMinSpeed,PatrolMaxSpeed}) -self.PatrolMinSpeed=PatrolMinSpeed -self.PatrolMaxSpeed=PatrolMaxSpeed -end -function AI_PATROL_ZONE:SetAltitude(PatrolFloorAltitude,PatrolCeilingAltitude) -self:F2({PatrolFloorAltitude,PatrolCeilingAltitude}) -self.PatrolFloorAltitude=PatrolFloorAltitude -self.PatrolCeilingAltitude=PatrolCeilingAltitude -end -function AI_PATROL_ZONE:SetDetectionOn() -self:F2() -self.DetectOn=true -end -function AI_PATROL_ZONE:SetDetectionOff() -self:F2() -self.DetectOn=false -end -function AI_PATROL_ZONE:SetStatusOff() -self:F2() -self.CheckStatus=false -end -function AI_PATROL_ZONE:SetDetectionActivated() -self:F2() -self:ClearDetectedUnits() -self.DetectActivated=true -self:__Detect(-self.DetectInterval) -end -function AI_PATROL_ZONE:SetDetectionDeactivated() -self:F2() -self:ClearDetectedUnits() -self.DetectActivated=false -end -function AI_PATROL_ZONE:SetRefreshTimeInterval(Seconds) -self:F2() -if Seconds then -self.DetectInterval=Seconds -else -self.DetectInterval=30 -end -end -function AI_PATROL_ZONE:SetDetectionZone(DetectionZone) -self:F2() -if DetectionZone then -self.DetectZone=DetectionZone -else -self.DetectZone=nil -end -end -function AI_PATROL_ZONE:GetDetectedUnits() -self:F2() -return self.DetectedUnits -end -function AI_PATROL_ZONE:ClearDetectedUnits() -self:F2() -self.DetectedUnits={} -end -function AI_PATROL_ZONE:ManageFuel(PatrolFuelThresholdPercentage,PatrolOutOfFuelOrbitTime) -self.PatrolFuelThresholdPercentage=PatrolFuelThresholdPercentage -self.PatrolOutOfFuelOrbitTime=PatrolOutOfFuelOrbitTime -return self -end -function AI_PATROL_ZONE:ManageDamage(PatrolDamageThreshold) -self.PatrolManageDamage=true -self.PatrolDamageThreshold=PatrolDamageThreshold -return self -end -function AI_PATROL_ZONE:onafterStart(Controllable,From,Event,To) -self:F2() -self:__Route(1) -self:__Status(60) -self:SetDetectionActivated() -self:HandleEvent(EVENTS.PilotDead,self.OnPilotDead) -self:HandleEvent(EVENTS.Crash,self.OnCrash) -self:HandleEvent(EVENTS.Ejection,self.OnEjection) -Controllable:OptionROEHoldFire() -Controllable:OptionROTVertical() -self.Controllable:OnReSpawn( -function(PatrolGroup) -self:T("ReSpawn") -self:__Reset(1) -self:__Route(5) -end -) -self:SetDetectionOn() -end -function AI_PATROL_ZONE:onbeforeDetect(Controllable,From,Event,To) -return self.DetectOn and self.DetectActivated -end -function AI_PATROL_ZONE:onafterDetect(Controllable,From,Event,To) -local Detected=false -local DetectedTargets=Controllable:GetDetectedTargets() -for TargetID,Target in pairs(DetectedTargets or{})do -local TargetObject=Target.object -if TargetObject and TargetObject:isExist()and TargetObject.id_<50000000 then -local TargetUnit=UNIT:Find(TargetObject) -if TargetUnit and TargetUnit:IsAlive()then -local TargetUnitName=TargetUnit:GetName() -if self.DetectionZone then -if TargetUnit:IsInZone(self.DetectionZone)then -self:T({"Detected ",TargetUnit}) -if self.DetectedUnits[TargetUnit]==nil then -self.DetectedUnits[TargetUnit]=true -end -Detected=true -end -else -if self.DetectedUnits[TargetUnit]==nil then -self.DetectedUnits[TargetUnit]=true -end -Detected=true -end -end -end -end -self:__Detect(-self.DetectInterval) -if Detected==true then -self:__Detected(1.5) -end -end -function AI_PATROL_ZONE:_NewPatrolRoute(AIControllable) -local PatrolZone=AIControllable:GetState(AIControllable,"PatrolZone") -PatrolZone:__Route(1) -end -function AI_PATROL_ZONE:onafterRoute(Controllable,From,Event,To) -self:F2() -if From=="RTB"then -return -end -if self.Controllable:IsAlive()then -local PatrolRoute={} -if self.Controllable:InAir()==false then -self:T("Not in the air, finding route path within PatrolZone") -local CurrentVec2=self.Controllable:GetVec2() -local CurrentAltitude=self.Controllable:GetUnit(1):GetAltitude() -local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y) -local ToPatrolZoneSpeed=self.PatrolMaxSpeed -local CurrentRoutePoint=CurrentPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TakeOffParking, -POINT_VEC3.RoutePointAction.FromParkingArea, -ToPatrolZoneSpeed, -true -) -PatrolRoute[#PatrolRoute+1]=CurrentRoutePoint -else -self:T("In the air, finding route path within PatrolZone") -local CurrentVec2=self.Controllable:GetVec2() -local CurrentAltitude=self.Controllable:GetUnit(1):GetAltitude() -local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y) -local ToPatrolZoneSpeed=self.PatrolMaxSpeed -local CurrentRoutePoint=CurrentPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -ToPatrolZoneSpeed, -true -) -PatrolRoute[#PatrolRoute+1]=CurrentRoutePoint -end -local ToTargetVec2=self.PatrolZone:GetRandomVec2() -self:T2(ToTargetVec2) -local ToTargetAltitude=math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude) -local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) -self:T2({self.PatrolMinSpeed,self.PatrolMaxSpeed,ToTargetSpeed}) -local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,ToTargetAltitude,ToTargetVec2.y) -local ToTargetRoutePoint=ToTargetPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -ToTargetSpeed, -true -) -PatrolRoute[#PatrolRoute+1]=ToTargetRoutePoint -self.Controllable:WayPointInitialize(PatrolRoute) -self.Controllable:SetState(self.Controllable,"PatrolZone",self) -self.Controllable:WayPointFunction(#PatrolRoute,1,"AI_PATROL_ZONE:_NewPatrolRoute") -self.Controllable:WayPointExecute(1,2) -end -end -function AI_PATROL_ZONE:onbeforeStatus() -return self.CheckStatus -end -function AI_PATROL_ZONE:onafterStatus() -self:F2() -if self.Controllable and self.Controllable:IsAlive()then -local RTB=false -local Fuel=self.Controllable:GetFuelMin() -if Fuel Engaging') -self:__Engage(1) -end -end -end -function AI_CAP_ZONE:onafterAbort(Controllable,From,Event,To) -Controllable:ClearTasks() -self:__Route(1) -end -function AI_CAP_ZONE:onafterEngage(Controllable,From,Event,To) -if Controllable and Controllable:IsAlive()then -local EngageRoute={} -local CurrentVec2=self.Controllable:GetVec2() -local CurrentAltitude=self.Controllable:GetUnit(1):GetAltitude() -local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y) -local ToEngageZoneSpeed=self.PatrolMaxSpeed -local CurrentRoutePoint=CurrentPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -ToEngageZoneSpeed, -true -) -EngageRoute[#EngageRoute+1]=CurrentRoutePoint -local ToTargetVec2=self.PatrolZone:GetRandomVec2() -self:T2(ToTargetVec2) -local ToTargetAltitude=math.random(self.EngageFloorAltitude,self.EngageCeilingAltitude) -local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) -self:T2({self.PatrolMinSpeed,self.PatrolMaxSpeed,ToTargetSpeed}) -local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,ToTargetAltitude,ToTargetVec2.y) -local ToPatrolRoutePoint=ToTargetPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -ToTargetSpeed, -true -) -EngageRoute[#EngageRoute+1]=ToPatrolRoutePoint -Controllable:OptionROEOpenFire() -Controllable:OptionROTEvadeFire() -local AttackTasks={} -for DetectedUnit,Detected in pairs(self.DetectedUnits)do -local DetectedUnit=DetectedUnit -self:T({DetectedUnit,DetectedUnit:IsAlive(),DetectedUnit:IsAir()}) -if DetectedUnit:IsAlive()and DetectedUnit:IsAir()then -if self.EngageZone then -if DetectedUnit:IsInZone(self.EngageZone)then -self:F({"Within Zone and Engaging ",DetectedUnit}) -AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit) -end -else -if self.EngageRange then -if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3())<=self.EngageRange then -self:F({"Within Range and Engaging",DetectedUnit}) -AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit) -end -else -AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit) -end -end -else -self.DetectedUnits[DetectedUnit]=nil -end -end -if#AttackTasks==0 then -self:F("No targets found -> Going back to Patrolling") -self:__Abort(1) -self:__Route(1) -self:SetDetectionActivated() -else -AttackTasks[#AttackTasks+1]=Controllable:TaskFunction("AI_CAP_ZONE.EngageRoute",self) -EngageRoute[1].task=Controllable:TaskCombo(AttackTasks) -self:SetDetectionDeactivated() -end -Controllable:Route(EngageRoute,0.5) -end -end -function AI_CAP_ZONE:onafterAccomplish(Controllable,From,Event,To) -self.Accomplished=true -self:SetDetectionOff() -end -function AI_CAP_ZONE:onafterDestroy(Controllable,From,Event,To,EventData) -if EventData.IniUnit then -self.DetectedUnits[EventData.IniUnit]=nil -end -end -function AI_CAP_ZONE:OnEventDead(EventData) -self:F({"EventDead",EventData}) -if EventData.IniDCSUnit then -if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit]then -self:__Destroy(1,EventData) -end -end -end -AI_CAS_ZONE={ -ClassName="AI_CAS_ZONE", -} -function AI_CAS_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageZone,PatrolAltType) -local self=BASE:Inherit(self,AI_PATROL_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)) -self.EngageZone=EngageZone -self.Accomplished=false -self:SetDetectionZone(self.EngageZone) -self:AddTransition({"Patrolling","Engaging"},"Engage","Engaging") -self:AddTransition("Engaging","Target","Engaging") -self:AddTransition("Engaging","Fired","Engaging") -self:AddTransition("*","Destroy","*") -self:AddTransition("Engaging","Abort","Patrolling") -self:AddTransition("Engaging","Accomplish","Patrolling") -return self -end -function AI_CAS_ZONE:SetEngageZone(EngageZone) -self:F2() -if EngageZone then -self.EngageZone=EngageZone -else -self.EngageZone=nil -end -end -function AI_CAS_ZONE:onafterStart(Controllable,From,Event,To) -self:GetParent(self).onafterStart(self,Controllable,From,Event,To) -self:HandleEvent(EVENTS.Dead) -self:SetDetectionDeactivated() -end -function AI_CAS_ZONE.EngageRoute(EngageGroup,Fsm) -EngageGroup:F({"AI_CAS_ZONE.EngageRoute:",EngageGroup:GetName()}) -if EngageGroup:IsAlive()then -Fsm:__Engage(1,Fsm.EngageSpeed,Fsm.EngageAltitude,Fsm.EngageWeaponExpend,Fsm.EngageAttackQty,Fsm.EngageDirection) -end -end -function AI_CAS_ZONE:onbeforeEngage(Controllable,From,Event,To) -if self.Accomplished==true then -return false -end -end -function AI_CAS_ZONE:onafterTarget(Controllable,From,Event,To) -if Controllable:IsAlive()then -local AttackTasks={} -for DetectedUnit,Detected in pairs(self.DetectedUnits)do -local DetectedUnit=DetectedUnit -if DetectedUnit:IsAlive()then -if DetectedUnit:IsInZone(self.EngageZone)then -if Detected==true then -self:F({"Target: ",DetectedUnit}) -self.DetectedUnits[DetectedUnit]=false -local AttackTask=Controllable:TaskAttackUnit(DetectedUnit,false,self.EngageWeaponExpend,self.EngageAttackQty,self.EngageDirection,self.EngageAltitude,nil) -self.Controllable:PushTask(AttackTask,1) -end -end -else -self.DetectedUnits[DetectedUnit]=nil -end -end -self:__Target(-10) -end -end -function AI_CAS_ZONE:onafterAbort(Controllable,From,Event,To) -Controllable:ClearTasks() -self:__Route(1) -end -function AI_CAS_ZONE:onafterEngage(Controllable,From,Event,To, -EngageSpeed, -EngageAltitude, -EngageWeaponExpend, -EngageAttackQty, -EngageDirection) -self:F("onafterEngage") -self.EngageSpeed=EngageSpeed or 400 -self.EngageAltitude=EngageAltitude or 2000 -self.EngageWeaponExpend=EngageWeaponExpend -self.EngageAttackQty=EngageAttackQty -self.EngageDirection=EngageDirection -if Controllable:IsAlive()then -Controllable:OptionROEOpenFire() -Controllable:OptionROTVertical() -local EngageRoute={} -local CurrentVec2=self.Controllable:GetVec2() -local CurrentAltitude=self.Controllable:GetUnit(1):GetAltitude() -local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y) -local ToEngageZoneSpeed=self.PatrolMaxSpeed -local CurrentRoutePoint=CurrentPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -self.EngageSpeed, -true -) -EngageRoute[#EngageRoute+1]=CurrentRoutePoint -local AttackTasks={} -for DetectedUnit,Detected in pairs(self.DetectedUnits)do -local DetectedUnit=DetectedUnit -self:T(DetectedUnit) -if DetectedUnit:IsAlive()then -if DetectedUnit:IsInZone(self.EngageZone)then -self:F({"Engaging ",DetectedUnit}) -AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit, -true, -EngageWeaponExpend, -EngageAttackQty, -EngageDirection -) -end -else -self.DetectedUnits[DetectedUnit]=nil -end -end -AttackTasks[#AttackTasks+1]=Controllable:TaskFunction("AI_CAS_ZONE.EngageRoute",self) -EngageRoute[#EngageRoute].task=Controllable:TaskCombo(AttackTasks) -local ToTargetVec2=self.EngageZone:GetRandomVec2() -self:T2(ToTargetVec2) -local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,self.EngageAltitude,ToTargetVec2.y) -local ToTargetRoutePoint=ToTargetPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -self.EngageSpeed, -true -) -EngageRoute[#EngageRoute+1]=ToTargetRoutePoint -Controllable:Route(EngageRoute,0.5) -self:SetRefreshTimeInterval(2) -self:SetDetectionActivated() -self:__Target(-2) -end -end -function AI_CAS_ZONE:onafterAccomplish(Controllable,From,Event,To) -self.Accomplished=true -self:SetDetectionDeactivated() -end -function AI_CAS_ZONE:onafterDestroy(Controllable,From,Event,To,EventData) -if EventData.IniUnit then -self.DetectedUnits[EventData.IniUnit]=nil -end -end -function AI_CAS_ZONE:OnEventDead(EventData) -self:F({"EventDead",EventData}) -if EventData.IniDCSUnit then -if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit]then -self:__Destroy(1,EventData) -end -end -end -AI_BAI_ZONE={ -ClassName="AI_BAI_ZONE", -} -function AI_BAI_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageZone,PatrolAltType) -local self=BASE:Inherit(self,AI_PATROL_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)) -self.EngageZone=EngageZone -self.Accomplished=false -self:SetDetectionZone(self.EngageZone) -self:SearchOn() -self:AddTransition({"Patrolling","Engaging"},"Engage","Engaging") -self:AddTransition("Engaging","Target","Engaging") -self:AddTransition("Engaging","Fired","Engaging") -self:AddTransition("*","Destroy","*") -self:AddTransition("Engaging","Abort","Patrolling") -self:AddTransition("Engaging","Accomplish","Patrolling") -return self -end -function AI_BAI_ZONE:SetEngageZone(EngageZone) -self:F2() -if EngageZone then -self.EngageZone=EngageZone -else -self.EngageZone=nil -end -end -function AI_BAI_ZONE:SearchOnOff(Search) -self.Search=Search -return self -end -function AI_BAI_ZONE:SearchOff() -self:SearchOnOff(false) -return self -end -function AI_BAI_ZONE:SearchOn() -self:SearchOnOff(true) -return self -end -function AI_BAI_ZONE:onafterStart(Controllable,From,Event,To) -self:GetParent(self).onafterStart(self,Controllable,From,Event,To) -self:HandleEvent(EVENTS.Dead) -self:SetDetectionDeactivated() -end -function _NewEngageRoute(AIControllable) -AIControllable:T("NewEngageRoute") -local EngageZone=AIControllable:GetState(AIControllable,"EngageZone") -EngageZone:__Engage(1,EngageZone.EngageSpeed,EngageZone.EngageAltitude,EngageZone.EngageWeaponExpend,EngageZone.EngageAttackQty,EngageZone.EngageDirection) -end -function AI_BAI_ZONE:onbeforeEngage(Controllable,From,Event,To) -if self.Accomplished==true then -return false -end -end -function AI_BAI_ZONE:onafterTarget(Controllable,From,Event,To) -self:F({"onafterTarget",self.Search,Controllable:IsAlive()}) -if Controllable:IsAlive()then -local AttackTasks={} -if self.Search==true then -for DetectedUnit,Detected in pairs(self.DetectedUnits)do -local DetectedUnit=DetectedUnit -if DetectedUnit:IsAlive()then -if DetectedUnit:IsInZone(self.EngageZone)then -if Detected==true then -self:F({"Target: ",DetectedUnit}) -self.DetectedUnits[DetectedUnit]=false -local AttackTask=Controllable:TaskAttackUnit(DetectedUnit,false,self.EngageWeaponExpend,self.EngageAttackQty,self.EngageDirection,self.EngageAltitude,nil) -self.Controllable:PushTask(AttackTask,1) -end -end -else -self.DetectedUnits[DetectedUnit]=nil -end -end -else -self:F("Attack zone") -local AttackTask=Controllable:TaskAttackMapObject( -self.EngageZone:GetPointVec2():GetVec2(), -true, -self.EngageWeaponExpend, -self.EngageAttackQty, -self.EngageDirection, -self.EngageAltitude -) -self.Controllable:PushTask(AttackTask,1) -end -self:__Target(-10) -end -end -function AI_BAI_ZONE:onafterAbort(Controllable,From,Event,To) -Controllable:ClearTasks() -self:__Route(1) -end -function AI_BAI_ZONE:onafterEngage(Controllable,From,Event,To, -EngageSpeed, -EngageAltitude, -EngageWeaponExpend, -EngageAttackQty, -EngageDirection) -self:F("onafterEngage") -self.EngageSpeed=EngageSpeed or 400 -self.EngageAltitude=EngageAltitude or 2000 -self.EngageWeaponExpend=EngageWeaponExpend -self.EngageAttackQty=EngageAttackQty -self.EngageDirection=EngageDirection -if Controllable:IsAlive()then -local EngageRoute={} -local CurrentVec2=self.Controllable:GetVec2() -local CurrentAltitude=self.Controllable:GetUnit(1):GetAltitude() -local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y) -local ToEngageZoneSpeed=self.PatrolMaxSpeed -local CurrentRoutePoint=CurrentPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -self.EngageSpeed, -true -) -EngageRoute[#EngageRoute+1]=CurrentRoutePoint -local AttackTasks={} -if self.Search==true then -for DetectedUnitID,DetectedUnitData in pairs(self.DetectedUnits)do -local DetectedUnit=DetectedUnitData -self:T(DetectedUnit) -if DetectedUnit:IsAlive()then -if DetectedUnit:IsInZone(self.EngageZone)then -self:F({"Engaging ",DetectedUnit}) -AttackTasks[#AttackTasks+1]=Controllable:TaskBombing( -DetectedUnit:GetPointVec2():GetVec2(), -true, -EngageWeaponExpend, -EngageAttackQty, -EngageDirection, -EngageAltitude -) -end -else -self.DetectedUnits[DetectedUnit]=nil -end -end -else -self:F("Attack zone") -AttackTasks[#AttackTasks+1]=Controllable:TaskAttackMapObject( -self.EngageZone:GetPointVec2():GetVec2(), -true, -EngageWeaponExpend, -EngageAttackQty, -EngageDirection, -EngageAltitude -) -end -EngageRoute[#EngageRoute].task=Controllable:TaskCombo(AttackTasks) -local ToTargetVec2=self.EngageZone:GetRandomVec2() -self:T2(ToTargetVec2) -local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,self.EngageAltitude,ToTargetVec2.y) -local ToTargetRoutePoint=ToTargetPointVec3:WaypointAir( -self.PatrolAltType, -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -self.EngageSpeed, -true -) -EngageRoute[#EngageRoute+1]=ToTargetRoutePoint -Controllable:OptionROEOpenFire() -Controllable:OptionROTVertical() -Controllable:WayPointInitialize(EngageRoute) -Controllable:SetState(Controllable,"EngageZone",self) -Controllable:WayPointFunction(#EngageRoute,1,"_NewEngageRoute") -Controllable:WayPointExecute(1) -self:SetRefreshTimeInterval(2) -self:SetDetectionActivated() -self:__Target(-2) -end -end -function AI_BAI_ZONE:onafterAccomplish(Controllable,From,Event,To) -self.Accomplished=true -self:SetDetectionDeactivated() -end -function AI_BAI_ZONE:onafterDestroy(Controllable,From,Event,To,EventData) -if EventData.IniUnit then -self.DetectedUnits[EventData.IniUnit]=nil -end -end -function AI_BAI_ZONE:OnEventDead(EventData) -self:F({"EventDead",EventData}) -if EventData.IniDCSUnit then -if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit]then -self:__Destroy(1,EventData) -end -end -end -AI_FORMATION={ -ClassName="AI_FORMATION", -FollowName=nil, -FollowUnit=nil, -FollowGroupSet=nil, -FollowMode=1, -MODE={ -FOLLOW=1, -MISSION=2, -}, -FollowScheduler=nil, -OptionROE=AI.Option.Air.val.ROE.OPEN_FIRE, -OptionReactionOnThreat=AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION, -dtFollow=0.5, -} -AI_FORMATION.__Enum={} -AI_FORMATION.__Enum.Formation={ -None=0, -Mission=1, -Line=2, -Trail=3, -Stack=4, -LeftLine=5, -RightLine=6, -LeftWing=7, -RightWing=8, -Vic=9, -Box=10, -} -AI_FORMATION.__Enum.Mode={ -Mission="M", -Formation="F", -Attack="A", -Reconnaissance="R", -} -AI_FORMATION.__Enum.ReportType={ -Airborne="*", -Airborne="A", -GroundRadar="R", -Ground="G", -} -function AI_FORMATION:New(FollowUnit,FollowGroupSet,FollowName,FollowBriefing) -local self=BASE:Inherit(self,FSM_SET:New(FollowGroupSet)) -self:F({FollowUnit,FollowGroupSet,FollowName}) -self.FollowUnit=FollowUnit -self.FollowGroupSet=FollowGroupSet -self.FollowGroupSet:ForEachGroup( -function(FollowGroup) -FollowGroup:SetState(self,"Mode",self.__Enum.Mode.Formation) -end -) -self:SetFlightModeFormation() -self:SetFlightRandomization(2) -self:SetStartState("None") -self:AddTransition("*","Stop","Stopped") -self:AddTransition({"None","Stopped"},"Start","Following") -self:AddTransition("*","FormationLine","*") -self:AddTransition("*","FormationTrail","*") -self:AddTransition("*","FormationStack","*") -self:AddTransition("*","FormationLeftLine","*") -self:AddTransition("*","FormationRightLine","*") -self:AddTransition("*","FormationLeftWing","*") -self:AddTransition("*","FormationRightWing","*") -self:AddTransition("*","FormationCenterWing","*") -self:AddTransition("*","FormationVic","*") -self:AddTransition("*","FormationBox","*") -self:AddTransition("*","Follow","Following") -self:FormationLeftLine(500,0,250,250) -self.FollowName=FollowName -self.FollowBriefing=FollowBriefing -self.CT1=0 -self.GT1=0 -self.FollowMode=AI_FORMATION.MODE.MISSION -return self -end -function AI_FORMATION:SetFollowTimeInterval(dt) -self.dtFollow=dt or 0.5 -return self -end -function AI_FORMATION:TestSmokeDirectionVector(SmokeDirection) -self.SmokeDirectionVector=(SmokeDirection==true)and true or false -return self -end -function AI_FORMATION:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace,Formation) -self:F({FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace,Formation}) -XStart=XStart or self.XStart -XSpace=XSpace or self.XSpace -YStart=YStart or self.YStart -YSpace=YSpace or self.YSpace -ZStart=ZStart or self.ZStart -ZSpace=ZSpace or self.ZSpace -FollowGroupSet:Flush(self) -local FollowSet=FollowGroupSet:GetSet() -local i=1 -for FollowID,FollowGroup in pairs(FollowSet)do -local PointVec3=POINT_VEC3:New() -PointVec3:SetX(XStart+i*XSpace) -PointVec3:SetY(YStart+i*YSpace) -PointVec3:SetZ(ZStart+i*ZSpace) -local Vec3=PointVec3:GetVec3() -FollowGroup:SetState(self,"FormationVec3",Vec3) -i=i+1 -FollowGroup:SetState(FollowGroup,"Formation",Formation) -end -return self -end -function AI_FORMATION:onafterFormationTrail(FollowGroupSet,From,Event,To,XStart,XSpace,YStart) -self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0,self.__Enum.Formation.Trail) -return self -end -function AI_FORMATION:onafterFormationStack(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace) -self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0,self.__Enum.Formation.Stack) -return self -end -function AI_FORMATION:onafterFormationLeftLine(FollowGroupSet,From,Event,To,XStart,YStart,ZStart,ZSpace) -self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace,self.__Enum.Formation.LeftLine) -return self -end -function AI_FORMATION:onafterFormationRightLine(FollowGroupSet,From,Event,To,XStart,YStart,ZStart,ZSpace) -self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightLine) -return self -end -function AI_FORMATION:onafterFormationLeftWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,ZStart,ZSpace) -self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.__Enum.Formation.LeftWing) -return self -end -function AI_FORMATION:onafterFormationRightWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,ZStart,ZSpace) -self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightWing) -return self -end -function AI_FORMATION:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace) -local FollowSet=FollowGroupSet:GetSet() -local i=0 -for FollowID,FollowGroup in pairs(FollowSet)do -local PointVec3=POINT_VEC3:New() -local Side=(i%2==0)and 1 or-1 -local Row=i/2+1 -PointVec3:SetX(XStart+Row*XSpace) -PointVec3:SetY(YStart) -PointVec3:SetZ(Side*(ZStart+i*ZSpace)) -local Vec3=PointVec3:GetVec3() -FollowGroup:SetState(self,"FormationVec3",Vec3) -i=i+1 -FollowGroup:SetState(FollowGroup,"Formation",self.__Enum.Formation.Vic) -end -return self -end -function AI_FORMATION:onafterFormationVic(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace) -self:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace) -return self -end -function AI_FORMATION:onafterFormationBox(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels) -local FollowSet=FollowGroupSet:GetSet() -local i=0 -for FollowID,FollowGroup in pairs(FollowSet)do -local PointVec3=POINT_VEC3:New() -local ZIndex=i%ZLevels -local XIndex=math.floor(i/ZLevels) -local YIndex=math.floor(i/ZLevels) -PointVec3:SetX(XStart+XIndex*XSpace) -PointVec3:SetY(YStart+YIndex*YSpace) -PointVec3:SetZ(-ZStart-(ZSpace*ZLevels/2)+ZSpace*ZIndex) -local Vec3=PointVec3:GetVec3() -FollowGroup:SetState(self,"FormationVec3",Vec3) -i=i+1 -FollowGroup:SetState(FollowGroup,"Formation",self.__Enum.Formation.Box) -end -return self -end -function AI_FORMATION:SetFlightRandomization(FlightRandomization) -self.FlightRandomization=FlightRandomization -return self -end -function AI_FORMATION:GetFlightMode(FollowGroup) -if FollowGroup then -FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode")) -FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Mission) -end -return FollowGroup:GetState(FollowGroup,"Mode") -end -function AI_FORMATION:SetFlightModeMission(FollowGroup) -if FollowGroup then -FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode")) -FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Mission) -else -self.FollowGroupSet:ForSomeGroupAlive( -function(FollowGroup) -FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode")) -FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Mission) -end -) -end -return self -end -function AI_FORMATION:SetFlightModeAttack(FollowGroup) -if FollowGroup then -FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode")) -FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Attack) -else -self.FollowGroupSet:ForSomeGroupAlive( -function(FollowGroup) -FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode")) -FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Attack) -end -) -end -return self -end -function AI_FORMATION:SetFlightModeFormation(FollowGroup) -if FollowGroup then -FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode")) -FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Formation) -else -self.FollowGroupSet:ForSomeGroupAlive( -function(FollowGroup) -FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode")) -FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Formation) -end -) -end -return self -end -function AI_FORMATION:onafterStop(FollowGroupSet,From,Event,To) -self:E("Stopping formation.") -end -function AI_FORMATION:onbeforeFollow(FollowGroupSet,From,Event,To) -if From=="Stopped"then -return false -end -return true -end -function AI_FORMATION:onenterFollowing(FollowGroupSet) -if self.FollowUnit:IsAlive()then -local ClientUnit=self.FollowUnit -local CT1,CT2,CV1,CV2 -CT1=ClientUnit:GetState(self,"CT1") -local CuVec3=ClientUnit:GetVec3() -if CT1==nil or CT1==0 then -ClientUnit:SetState(self,"CV1",CuVec3) -ClientUnit:SetState(self,"CT1",timer.getTime()) -else -CT1=ClientUnit:GetState(self,"CT1") -CT2=timer.getTime() -CV1=ClientUnit:GetState(self,"CV1") -CV2=CuVec3 -ClientUnit:SetState(self,"CT1",CT2) -ClientUnit:SetState(self,"CV1",CV2) -end -for _,_group in pairs(FollowGroupSet:GetSet())do -local group=_group -if group and group:IsAlive()then -self:FollowMe(group,ClientUnit,CT1,CV1,CT2,CV2) -end -end -self:__Follow(-self.dtFollow) -end -end -function AI_FORMATION:FollowMe(FollowGroup,ClientUnit,CT1,CV1,CT2,CV2) -if FollowGroup:GetState(FollowGroup,"Mode")==self.__Enum.Mode.Formation and not self:Is("Stopped")then -self:T({Mode=FollowGroup:GetState(FollowGroup,"Mode")}) -FollowGroup:OptionROTEvadeFire() -FollowGroup:OptionROEReturnFire() -local GroupUnit=FollowGroup:GetUnit(1) -local GuVec3=GroupUnit:GetVec3() -local FollowFormation=FollowGroup:GetState(self,"FormationVec3") -if FollowFormation then -local FollowDistance=FollowFormation.x -local GT1=GroupUnit:GetState(self,"GT1") -if CT1==nil or CT1==0 or GT1==nil or GT1==0 then -GroupUnit:SetState(self,"GV1",GuVec3) -GroupUnit:SetState(self,"GT1",timer.getTime()) -else -local CD=((CV2.x-CV1.x)^2+(CV2.y-CV1.y)^2+(CV2.z-CV1.z)^2)^0.5 -local CT=CT2-CT1 -local CS=(3600/CT)*(CD/1000)/3.6 -local CDv={x=CV2.x-CV1.x,y=CV2.y-CV1.y,z=CV2.z-CV1.z} -local Ca=math.atan2(CDv.x,CDv.z) -local GT1=GroupUnit:GetState(self,"GT1") -local GT2=timer.getTime() -local GV1=GroupUnit:GetState(self,"GV1") -local GV2=GuVec3 -GV2.x=GV2.x+math.random(-self.FlightRandomization/2,self.FlightRandomization/2) -GV2.y=GV2.y+math.random(-self.FlightRandomization/2,self.FlightRandomization/2) -GV2.z=GV2.z+math.random(-self.FlightRandomization/2,self.FlightRandomization/2) -GroupUnit:SetState(self,"GT1",GT2) -GroupUnit:SetState(self,"GV1",GV2) -local GD=((GV2.x-GV1.x)^2+(GV2.y-GV1.y)^2+(GV2.z-GV1.z)^2)^0.5 -local GT=GT2-GT1 -local GDv={x=GV2.x-CV1.x,y=GV2.y-CV1.y,z=GV2.z-CV1.z} -local Alpha_T=math.atan2(GDv.x,GDv.z)-math.atan2(CDv.x,CDv.z) -local Alpha_R=(Alpha_T<0)and Alpha_T+2*math.pi or Alpha_T -local Position=math.cos(Alpha_R) -local GD=((GDv.x)^2+(GDv.z)^2)^0.5 -local Distance=GD*Position+-CS*0.5 -local GV={x=GV2.x-CV2.x,y=GV2.y-CV2.y,z=GV2.z-CV2.z} -local GH2={x=GV2.x,y=CV2.y+FollowFormation.y,z=GV2.z} -local alpha=math.atan2(GV.x,GV.z) -local GVx=FollowFormation.z*math.cos(Ca)+FollowFormation.x*math.sin(Ca) -local GVz=FollowFormation.x*math.cos(Ca)-FollowFormation.z*math.sin(Ca) -local Inclination=(Distance+FollowFormation.x)/10 -if Inclination<-30 then -Inclination=-30 -end -local CVI={ -x=CV2.x+CS*10*math.sin(Ca), -y=GH2.y+Inclination, -y=GH2.y, -z=CV2.z+CS*10*math.cos(Ca), -} -local DV={x=CV2.x-CVI.x,y=CV2.y-CVI.y,z=CV2.z-CVI.z} -local DVu={x=DV.x/FollowDistance,y=DV.y,z=DV.z/FollowDistance} -local GDV={x=CVI.x,y=CVI.y,z=CVI.z} -local ADDx=FollowFormation.x*math.cos(alpha)-FollowFormation.z*math.sin(alpha) -local ADDz=FollowFormation.z*math.cos(alpha)+FollowFormation.x*math.sin(alpha) -local GDV_Formation={ -x=GDV.x-GVx, -y=GDV.y, -z=GDV.z-GVz -} -if self.SmokeDirectionVector==true then -trigger.action.smoke(GDV,trigger.smokeColor.Green) -trigger.action.smoke(GDV_Formation,trigger.smokeColor.White) -end -local Time=120 -local Speed=-(Distance+FollowFormation.x)/Time -if Distance>-10000 then -Speed=-(Distance+FollowFormation.x)/60 -end -if Distance>-2500 then -Speed=-(Distance+FollowFormation.x)/20 -end -local GS=Speed+CS -FollowGroup:RouteToVec3(GDV_Formation,GS) -end -end -end -end -AI_ESCORT={ -ClassName="AI_ESCORT", -EscortName=nil, -EscortUnit=nil, -EscortGroup=nil, -EscortMode=1, -Targets={}, -FollowScheduler=nil, -ReportTargets=true, -OptionROE=AI.Option.Air.val.ROE.OPEN_FIRE, -OptionReactionOnThreat=AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION, -SmokeDirectionVector=false, -TaskPoints={} -} -AI_ESCORT.Detection=nil -function AI_ESCORT:New(EscortUnit,EscortGroupSet,EscortName,EscortBriefing) -local self=BASE:Inherit(self,AI_FORMATION:New(EscortUnit,EscortGroupSet,EscortName,EscortBriefing)) -self:F({EscortUnit,EscortGroupSet}) -self.PlayerUnit=self.FollowUnit -self.PlayerGroup=self.FollowUnit:GetGroup() -self.EscortName=EscortName -self.EscortGroupSet=EscortGroupSet -self.EscortGroupSet:SetSomeIteratorLimit(8) -self.EscortBriefing=EscortBriefing -self.Menu={} -self.FollowDistance=100 -self.CT1=0 -self.GT1=0 -EscortGroupSet:ForEachGroup( -function(EscortGroup) -if not self.PlayerUnit._EscortGroups then -self.PlayerUnit._EscortGroups={} -end -if not self.PlayerUnit._EscortGroups[EscortGroup:GetName()]then -self.PlayerUnit._EscortGroups[EscortGroup:GetName()]={} -self.PlayerUnit._EscortGroups[EscortGroup:GetName()].EscortGroup=EscortGroup -self.PlayerUnit._EscortGroups[EscortGroup:GetName()].EscortName=self.EscortName -self.PlayerUnit._EscortGroups[EscortGroup:GetName()].Detection=self.Detection -end -end -) -self:SetFlightReportType(self.__Enum.ReportType.All) -return self -end -function AI_ESCORT:_InitFlightMenus() -self:SetFlightMenuJoinUp() -self:SetFlightMenuFormation("Trail") -self:SetFlightMenuFormation("Stack") -self:SetFlightMenuFormation("LeftLine") -self:SetFlightMenuFormation("RightLine") -self:SetFlightMenuFormation("LeftWing") -self:SetFlightMenuFormation("RightWing") -self:SetFlightMenuFormation("Vic") -self:SetFlightMenuFormation("Box") -self:SetFlightMenuHoldAtEscortPosition() -self:SetFlightMenuHoldAtLeaderPosition() -self:SetFlightMenuFlare() -self:SetFlightMenuSmoke() -self:SetFlightMenuROE() -self:SetFlightMenuROT() -self:SetFlightMenuTargets() -self:SetFlightMenuReportType() -end -function AI_ESCORT:_InitEscortMenus(EscortGroup) -EscortGroup.EscortMenu=MENU_GROUP:New(self.PlayerGroup,EscortGroup:GetCallsign(),self.MainMenu) -self:SetEscortMenuJoinUp(EscortGroup) -self:SetEscortMenuResumeMission(EscortGroup) -self:SetEscortMenuHoldAtEscortPosition(EscortGroup) -self:SetEscortMenuHoldAtLeaderPosition(EscortGroup) -self:SetEscortMenuFlare(EscortGroup) -self:SetEscortMenuSmoke(EscortGroup) -self:SetEscortMenuROE(EscortGroup) -self:SetEscortMenuROT(EscortGroup) -self:SetEscortMenuTargets(EscortGroup) -end -function AI_ESCORT:_InitEscortRoute(EscortGroup) -EscortGroup.MissionRoute=EscortGroup:GetTaskRoute() -end -function AI_ESCORT:onafterStart(EscortGroupSet) -self:F() -EscortGroupSet:ForEachGroup( -function(EscortGroup) -EscortGroup:WayPointInitialize() -EscortGroup:OptionROTVertical() -EscortGroup:OptionROEOpenFire() -end -) -local LeaderEscort=EscortGroupSet:GetFirst() -if LeaderEscort then -local Report=REPORT:New("Escort reporting:") -Report:Add("Joining Up "..EscortGroupSet:GetUnitTypeNames():Text(", ").." from "..LeaderEscort:GetCoordinate():ToString(self.PlayerUnit)) -LeaderEscort:MessageTypeToGroup(Report:Text(),MESSAGE.Type.Information,self.PlayerUnit) -end -self.Detection=DETECTION_AREAS:New(EscortGroupSet,5000) -self.Detection:InitDetectVisual(true) -self.Detection:InitDetectIRST(true) -self.Detection:InitDetectOptical(true) -self.Detection:InitDetectRadar(true) -self.Detection:InitDetectRWR(true) -self.Detection:SetAcceptRange(100000) -self.Detection:__Start(30) -self.MainMenu=MENU_GROUP:New(self.PlayerGroup,self.EscortName) -self.FlightMenu=MENU_GROUP:New(self.PlayerGroup,"Flight",self.MainMenu) -self:_InitFlightMenus() -self.EscortGroupSet:ForSomeGroupAlive( -function(EscortGroup) -self:_InitEscortMenus(EscortGroup) -self:_InitEscortRoute(EscortGroup) -self:SetFlightModeFormation(EscortGroup) -function EscortGroup:OnEventDeadOrCrash(EventData) -self:F({"EventDead",EventData}) -self.EscortMenu:Remove() -end -EscortGroup:HandleEvent(EVENTS.Dead,EscortGroup.OnEventDeadOrCrash) -EscortGroup:HandleEvent(EVENTS.Crash,EscortGroup.OnEventDeadOrCrash) -end -) -end -function AI_ESCORT:onafterStop(EscortGroupSet) -self:F() -EscortGroupSet:ForEachGroup( -function(EscortGroup) -EscortGroup:WayPointInitialize() -EscortGroup:OptionROTVertical() -EscortGroup:OptionROEOpenFire() -end -) -self.Detection:Stop() -self.MainMenu:Remove() -end -function AI_ESCORT:SetDetection(Detection) -self.Detection=Detection -self.EscortGroup.Detection=self.Detection -self.PlayerUnit._EscortGroups[self.EscortGroup:GetName()].Detection=self.EscortGroup.Detection -Detection:__Start(1) -end -function AI_ESCORT:TestSmokeDirectionVector(SmokeDirection) -self.SmokeDirectionVector=(SmokeDirection==true)and true or false -end -function AI_ESCORT:MenusHelicopters(XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels) -self:F() -self.XStart=XStart or 50 -self.XSpace=XSpace or 50 -self.YStart=YStart or 50 -self.YSpace=YSpace or 50 -self.ZStart=ZStart or 50 -self.ZSpace=ZSpace or 50 -self.ZLevels=ZLevels or 10 -self:MenuJoinUp() -self:MenuFormationTrail(self.XStart,self.XSpace,self.YStart) -self:MenuFormationStack(self.XStart,self.XSpace,self.YStart,self.YSpace) -self:MenuFormationLeftLine(self.XStart,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationRightLine(self.XStart,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationLeftWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationRightWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationVic(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace) -self:MenuFormationBox(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace,self.ZLevels) -self:MenuHoldAtEscortPosition(30) -self:MenuHoldAtEscortPosition(100) -self:MenuHoldAtEscortPosition(500) -self:MenuHoldAtLeaderPosition(30,500) -self:MenuFlare() -self:MenuSmoke() -self:MenuTargets(60) -self:MenuAssistedAttack() -self:MenuROE() -self:MenuROT() -return self -end -function AI_ESCORT:MenusAirplanes(XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels) -self:F() -self.XStart=XStart or 50 -self.XSpace=XSpace or 50 -self.YStart=YStart or 50 -self.YSpace=YSpace or 50 -self.ZStart=ZStart or 50 -self.ZSpace=ZSpace or 50 -self.ZLevels=ZLevels or 10 -self:MenuJoinUp() -self:MenuFormationTrail(self.XStart,self.XSpace,self.YStart) -self:MenuFormationStack(self.XStart,self.XSpace,self.YStart,self.YSpace) -self:MenuFormationLeftLine(self.XStart,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationRightLine(self.XStart,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationLeftWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationRightWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace) -self:MenuFormationVic(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace) -self:MenuFormationBox(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace,self.ZLevels) -self:MenuHoldAtEscortPosition(1000,500) -self:MenuHoldAtLeaderPosition(1000,500) -self:MenuFlare() -self:MenuSmoke() -self:MenuTargets(60) -self:MenuAssistedAttack() -self:MenuROE() -self:MenuROT() -return self -end -function AI_ESCORT:SetFlightMenuFormation(Formation) -local FormationID="Formation"..Formation -local MenuFormation=self.Menu[FormationID] -if MenuFormation then -local Arguments=MenuFormation.Arguments -local FlightMenuFormation=MENU_GROUP:New(self.PlayerGroup,"Formation",self.MainMenu) -local MenuFlightFormationID=MENU_GROUP_COMMAND:New(self.PlayerGroup,Formation,FlightMenuFormation, -function(self,Formation,...) -self.EscortGroupSet:ForSomeGroupAlive( -function(EscortGroup,self,Formation,Arguments) -if EscortGroup:IsAir()then -self:E({FormationID=FormationID}) -self[FormationID](self,unpack(Arguments)) -end -end,self,Formation,Arguments -) -end,self,Formation,Arguments -) -end -return self -end -function AI_ESCORT:MenuFormation(Formation,...) -local FormationID="Formation"..Formation -self.Menu[FormationID]=self.Menu[FormationID]or{} -self.Menu[FormationID].Arguments=arg -end -function AI_ESCORT:MenuFormationTrail(XStart,XSpace,YStart) -self:MenuFormation("Trail",XStart,XSpace,YStart) -return self -end -function AI_ESCORT:MenuFormationStack(XStart,XSpace,YStart,YSpace) -self:MenuFormation("Stack",XStart,XSpace,YStart,YSpace) -return self -end -function AI_ESCORT:MenuFormationLeftLine(XStart,YStart,ZStart,ZSpace) -self:MenuFormation("LeftLine",XStart,YStart,ZStart,ZSpace) -return self -end -function AI_ESCORT:MenuFormationRightLine(XStart,YStart,ZStart,ZSpace) -self:MenuFormation("RightLine",XStart,YStart,ZStart,ZSpace) -return self -end -function AI_ESCORT:MenuFormationLeftWing(XStart,XSpace,YStart,ZStart,ZSpace) -self:MenuFormation("LeftWing",XStart,XSpace,YStart,ZStart,ZSpace) -return self -end -function AI_ESCORT:MenuFormationRightWing(XStart,XSpace,YStart,ZStart,ZSpace) -self:MenuFormation("RightWing",XStart,XSpace,YStart,ZStart,ZSpace) -return self -end -function AI_ESCORT:MenuFormationCenterWing(XStart,XSpace,YStart,YSpace,ZStart,ZSpace) -self:MenuFormation("CenterWing",XStart,XSpace,YStart,YSpace,ZStart,ZSpace) -return self -end -function AI_ESCORT:MenuFormationVic(XStart,XSpace,YStart,YSpace,ZStart,ZSpace) -self:MenuFormation("Vic",XStart,XSpace,YStart,YSpace,ZStart,ZSpace) -return self -end -function AI_ESCORT:MenuFormationBox(XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels) -self:MenuFormation("Box",XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels) -return self -end -function AI_ESCORT:SetFlightMenuJoinUp() -if self.Menu.JoinUp==true then -local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu) -local FlightMenuJoinUp=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Join Up",FlightMenuReportNavigation,AI_ESCORT._FlightJoinUp,self) -end -end -function AI_ESCORT:SetEscortMenuJoinUp(EscortGroup) -if self.Menu.JoinUp==true then -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu) -local EscortMenuJoinUp=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Join Up",EscortMenuReportNavigation,AI_ESCORT._JoinUp,self,EscortGroup) -end -end -end -function AI_ESCORT:MenuJoinUp() -self.Menu.JoinUp=true -return self -end -function AI_ESCORT:SetFlightMenuHoldAtEscortPosition() -for _,MenuHoldAtEscortPosition in pairs(self.Menu.HoldAtEscortPosition)do -local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu) -local FlightMenuHoldPosition=MENU_GROUP_COMMAND -:New( -self.PlayerGroup, -MenuHoldAtEscortPosition.MenuText, -FlightMenuReportNavigation, -AI_ESCORT._FlightHoldPosition, -self, -nil, -MenuHoldAtEscortPosition.Height, -MenuHoldAtEscortPosition.Speed -) -end -return self -end -function AI_ESCORT:SetEscortMenuHoldAtEscortPosition(EscortGroup) -for _,HoldAtEscortPosition in pairs(self.Menu.HoldAtEscortPosition)do -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu) -local EscortMenuHoldPosition=MENU_GROUP_COMMAND -:New( -self.PlayerGroup, -HoldAtEscortPosition.MenuText, -EscortMenuReportNavigation, -AI_ESCORT._HoldPosition, -self, -EscortGroup, -EscortGroup, -HoldAtEscortPosition.Height, -HoldAtEscortPosition.Speed -) -end -end -return self -end -function AI_ESCORT:MenuHoldAtEscortPosition(Height,Speed,MenuTextFormat) -self:F({Height,Speed,MenuTextFormat}) -if not Height then -Height=30 -end -if not Speed then -Speed=0 -end -local MenuText="" -if not MenuTextFormat then -if Speed==0 then -MenuText=string.format("Hold at %d meter",Height) -else -MenuText=string.format("Hold at %d meter at %d",Height,Speed) -end -else -if Speed==0 then -MenuText=string.format(MenuTextFormat,Height) -else -MenuText=string.format(MenuTextFormat,Height,Speed) -end -end -self.Menu.HoldAtEscortPosition=self.Menu.HoldAtEscortPosition or{} -self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition+1]={} -self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].Height=Height -self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].Speed=Speed -self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].MenuText=MenuText -return self -end -function AI_ESCORT:SetFlightMenuHoldAtLeaderPosition() -for _,MenuHoldAtLeaderPosition in pairs(self.Menu.HoldAtLeaderPosition)do -local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu) -local FlightMenuHoldAtLeaderPosition=MENU_GROUP_COMMAND -:New( -self.PlayerGroup, -MenuHoldAtLeaderPosition.MenuText, -FlightMenuReportNavigation, -AI_ESCORT._FlightHoldPosition, -self, -self.PlayerGroup, -MenuHoldAtLeaderPosition.Height, -MenuHoldAtLeaderPosition.Speed -) -end -return self -end -function AI_ESCORT:SetEscortMenuHoldAtLeaderPosition(EscortGroup) -for _,HoldAtLeaderPosition in pairs(self.Menu.HoldAtLeaderPosition)do -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu) -local EscortMenuHoldAtLeaderPosition=MENU_GROUP_COMMAND -:New( -self.PlayerGroup, -HoldAtLeaderPosition.MenuText, -EscortMenuReportNavigation, -AI_ESCORT._HoldPosition, -self, -self.PlayerGroup, -EscortGroup, -HoldAtLeaderPosition.Height, -HoldAtLeaderPosition.Speed -) -end -end -return self -end -function AI_ESCORT:MenuHoldAtLeaderPosition(Height,Speed,MenuTextFormat) -self:F({Height,Speed,MenuTextFormat}) -if not Height then -Height=30 -end -if not Speed then -Speed=0 -end -local MenuText="" -if not MenuTextFormat then -if Speed==0 then -MenuText=string.format("Rejoin and hold at %d meter",Height) -else -MenuText=string.format("Rejoin and hold at %d meter at %d",Height,Speed) -end -else -if Speed==0 then -MenuText=string.format(MenuTextFormat,Height) -else -MenuText=string.format(MenuTextFormat,Height,Speed) -end -end -self.Menu.HoldAtLeaderPosition=self.Menu.HoldAtLeaderPosition or{} -self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition+1]={} -self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].Height=Height -self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].Speed=Speed -self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].MenuText=MenuText -return self -end -function AI_ESCORT:MenuScanForTargets(Height,Seconds,MenuTextFormat) -self:F({Height,Seconds,MenuTextFormat}) -if self.EscortGroup:IsAir()then -if not self.EscortMenuScan then -self.EscortMenuScan=MENU_GROUP:New(self.PlayerGroup,"Scan for targets",self.EscortMenu) -end -if not Height then -Height=100 -end -if not Seconds then -Seconds=30 -end -local MenuText="" -if not MenuTextFormat then -if Seconds==0 then -MenuText=string.format("At %d meter",Height) -else -MenuText=string.format("At %d meter for %d seconds",Height,Seconds) -end -else -if Seconds==0 then -MenuText=string.format(MenuTextFormat,Height) -else -MenuText=string.format(MenuTextFormat,Height,Seconds) -end -end -if not self.EscortMenuScanForTargets then -self.EscortMenuScanForTargets={} -end -self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1]=MENU_GROUP_COMMAND -:New( -self.PlayerGroup, -MenuText, -self.EscortMenuScan, -AI_ESCORT._ScanTargets, -self, -30 -) -end -return self -end -function AI_ESCORT:SetFlightMenuFlare() -for _,MenuFlare in pairs(self.Menu.Flare)do -local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu) -local FlightMenuFlare=MENU_GROUP:New(self.PlayerGroup,MenuFlare.MenuText,FlightMenuReportNavigation) -local FlightMenuFlareGreenFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.Green,"Released a green flare!") -local FlightMenuFlareRedFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.Red,"Released a red flare!") -local FlightMenuFlareWhiteFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.White,"Released a white flare!") -local FlightMenuFlareYellowFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release yellow flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.Yellow,"Released a yellow flare!") -end -return self -end -function AI_ESCORT:SetEscortMenuFlare(EscortGroup) -for _,MenuFlare in pairs(self.Menu.Flare)do -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu) -local EscortMenuFlare=MENU_GROUP:New(self.PlayerGroup,MenuFlare.MenuText,EscortMenuReportNavigation) -local EscortMenuFlareGreen=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.Green,"Released a green flare!") -local EscortMenuFlareRed=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.Red,"Released a red flare!") -local EscortMenuFlareWhite=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.White,"Released a white flare!") -local EscortMenuFlareYellow=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release yellow flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.Yellow,"Released a yellow flare!") -end -end -return self -end -function AI_ESCORT:MenuFlare(MenuTextFormat) -self:F() -local MenuText="" -if not MenuTextFormat then -MenuText="Flare" -else -MenuText=MenuTextFormat -end -self.Menu.Flare=self.Menu.Flare or{} -self.Menu.Flare[#self.Menu.Flare+1]={} -self.Menu.Flare[#self.Menu.Flare].MenuText=MenuText -return self -end -function AI_ESCORT:SetFlightMenuSmoke() -for _,MenuSmoke in pairs(self.Menu.Smoke)do -local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu) -local FlightMenuSmoke=MENU_GROUP:New(self.PlayerGroup,MenuSmoke.MenuText,FlightMenuReportNavigation) -local FlightMenuSmokeGreenFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Green,"Releasing green smoke!") -local FlightMenuSmokeRedFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Red,"Releasing red smoke!") -local FlightMenuSmokeWhiteFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.White,"Releasing white smoke!") -local FlightMenuSmokeOrangeFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release orange smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Orange,"Releasing orange smoke!") -local FlightMenuSmokeBlueFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release blue smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Blue,"Releasing blue smoke!") -end -return self -end -function AI_ESCORT:SetEscortMenuSmoke(EscortGroup) -for _,MenuSmoke in pairs(self.Menu.Smoke)do -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu) -local EscortMenuSmoke=MENU_GROUP:New(self.PlayerGroup,MenuSmoke.MenuText,EscortMenuReportNavigation) -local EscortMenuSmokeGreen=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Green,"Releasing green smoke!") -local EscortMenuSmokeRed=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Red,"Releasing red smoke!") -local EscortMenuSmokeWhite=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.White,"Releasing white smoke!") -local EscortMenuSmokeOrange=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release orange smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Orange,"Releasing orange smoke!") -local EscortMenuSmokeBlue=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release blue smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Blue,"Releasing blue smoke!") -end -end -return self -end -function AI_ESCORT:MenuSmoke(MenuTextFormat) -self:F() -local MenuText="" -if not MenuTextFormat then -MenuText="Smoke" -else -MenuText=MenuTextFormat -end -self.Menu.Smoke=self.Menu.Smoke or{} -self.Menu.Smoke[#self.Menu.Smoke+1]={} -self.Menu.Smoke[#self.Menu.Smoke].MenuText=MenuText -return self -end -function AI_ESCORT:SetFlightMenuReportType() -local FlightMenuReportTargets=MENU_GROUP:New(self.PlayerGroup,"Report targets",self.FlightMenu) -local MenuStamp=FlightMenuReportTargets:GetStamp() -local FlightReportType=self:GetFlightReportType() -if FlightReportType~=self.__Enum.ReportType.All then -local FlightMenuReportTargetsAll=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report all targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeAll,self) -:SetTag("ReportType") -:SetStamp(MenuStamp) -end -if FlightReportType==self.__Enum.ReportType.All or FlightReportType~=self.__Enum.ReportType.Airborne then -local FlightMenuReportTargetsAirborne=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report airborne targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeAirborne,self) -:SetTag("ReportType") -:SetStamp(MenuStamp) -end -if FlightReportType==self.__Enum.ReportType.All or FlightReportType~=self.__Enum.ReportType.GroundRadar then -local FlightMenuReportTargetsGroundRadar=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report gound radar targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeGroundRadar,self) -:SetTag("ReportType") -:SetStamp(MenuStamp) -end -if FlightReportType==self.__Enum.ReportType.All or FlightReportType~=self.__Enum.ReportType.Ground then -local FlightMenuReportTargetsGround=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report ground targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeGround,self) -:SetTag("ReportType") -:SetStamp(MenuStamp) -end -FlightMenuReportTargets:RemoveSubMenus(MenuStamp,"ReportType") -end -function AI_ESCORT:SetFlightMenuTargets() -local FlightMenuReportTargets=MENU_GROUP:New(self.PlayerGroup,"Report targets",self.FlightMenu) -local FlightMenuReportTargetsNow=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets now!",FlightMenuReportTargets,AI_ESCORT._FlightReportNearbyTargetsNow,self) -local FlightMenuReportTargetsOn=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets on",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportNearbyTargets,self,true) -local FlightMenuReportTargetsOff=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets off",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportNearbyTargets,self,false) -self.FlightMenuAttack=MENU_GROUP:New(self.PlayerGroup,"Attack targets",self.FlightMenu) -local FlightMenuAttackNearby=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Attack nearest targets",self.FlightMenuAttack,AI_ESCORT._FlightAttackNearestTarget,self):SetTag("Attack") -local FlightMenuAttackNearbyAir=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Attack nearest airborne targets",self.FlightMenuAttack,AI_ESCORT._FlightAttackNearestTarget,self,self.__Enum.ReportType.Air):SetTag("Attack") -local FlightMenuAttackNearbyGround=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Attack nearest ground targets",self.FlightMenuAttack,AI_ESCORT._FlightAttackNearestTarget,self,self.__Enum.ReportType.Ground):SetTag("Attack") -for _,MenuTargets in pairs(self.Menu.Targets)do -MenuTargets.FlightReportTargetsScheduler=SCHEDULER:New(self,self._FlightReportTargetsScheduler,{},MenuTargets.Interval,MenuTargets.Interval) -end -return self -end -function AI_ESCORT:SetEscortMenuTargets(EscortGroup) -for _,MenuTargets in pairs(self.Menu.Targets)do -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -EscortGroup.EscortMenuReportNearbyTargetsNow=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets",EscortGroup.EscortMenu,AI_ESCORT._ReportNearbyTargetsNow,self,EscortGroup,true) -EscortGroup.ReportTargetsScheduler=SCHEDULER:New(self,self._ReportTargetsScheduler,{EscortGroup},1,MenuTargets.Interval) -EscortGroup.ResumeScheduler=SCHEDULER:New(self,self._ResumeScheduler,{EscortGroup},1,60) -end -end -return self -end -function AI_ESCORT:MenuTargets(Seconds) -self:F({Seconds}) -if not Seconds then -Seconds=30 -end -self.Menu.Targets=self.Menu.Targets or{} -self.Menu.Targets[#self.Menu.Targets+1]={} -self.Menu.Targets[#self.Menu.Targets].Interval=Seconds -return self -end -function AI_ESCORT:MenuAssistedAttack() -self:F() -self.EscortGroupSet:ForSomeGroupAlive( -function(EscortGroup) -if not EscortGroup:IsAir()then -self.EscortMenuTargetAssistance=MENU_GROUP:New(self.PlayerGroup,"Request assistance from",EscortGroup.EscortMenu) -end -end -) -return self -end -function AI_ESCORT:SetFlightMenuROE() -for _,MenuROE in pairs(self.Menu.ROE)do -local FlightMenuROE=MENU_GROUP:New(self.PlayerGroup,"Rule Of Engagement",self.FlightMenu) -local FlightMenuROEHoldFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Hold fire",FlightMenuROE,AI_ESCORT._FlightROEHoldFire,self,"Holding weapons!") -local FlightMenuROEReturnFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Return fire",FlightMenuROE,AI_ESCORT._FlightROEReturnFire,self,"Returning fire!") -local FlightMenuROEOpenFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open Fire",FlightMenuROE,AI_ESCORT._FlightROEOpenFire,self,"Open fire at designated targets!") -local FlightMenuROEWeaponFree=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Engage all targets",FlightMenuROE,AI_ESCORT._FlightROEWeaponFree,self,"Engaging all targets!") -end -return self -end -function AI_ESCORT:SetEscortMenuROE(EscortGroup) -for _,MenuROE in pairs(self.Menu.ROE)do -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -local EscortMenuROE=MENU_GROUP:New(self.PlayerGroup,"Rule Of Engagement",EscortGroup.EscortMenu) -if EscortGroup:OptionROEHoldFirePossible()then -local EscortMenuROEHoldFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Hold fire",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEHoldFire,"Holding weapons!") -end -if EscortGroup:OptionROEReturnFirePossible()then -local EscortMenuROEReturnFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Return fire",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEReturnFire,"Returning fire!") -end -if EscortGroup:OptionROEOpenFirePossible()then -EscortGroup.EscortMenuROEOpenFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open Fire",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEOpenFire,"Opening fire on designated targets!!") -end -if EscortGroup:OptionROEWeaponFreePossible()then -EscortGroup.EscortMenuROEWeaponFree=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Engage all targets",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEWeaponFree,"Opening fire on targets of opportunity!") -end -end -end -return self -end -function AI_ESCORT:MenuROE() -self:F() -self.Menu.ROE=self.Menu.ROE or{} -self.Menu.ROE[#self.Menu.ROE+1]={} -return self -end -function AI_ESCORT:SetFlightMenuROT() -for _,MenuROT in pairs(self.Menu.ROT)do -local FlightMenuROT=MENU_GROUP:New(self.PlayerGroup,"Reaction On Threat",self.FlightMenu) -local FlightMenuROTNoReaction=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Fight until death",FlightMenuROT,AI_ESCORT._FlightROTNoReaction,self,"Fighting until death!") -local FlightMenuROTPassiveDefense=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Use flares, chaff and jammers",FlightMenuROT,AI_ESCORT._FlightROTPassiveDefense,self,"Defending using jammers, chaff and flares!") -local FlightMenuROTEvadeFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open fire",FlightMenuROT,AI_ESCORT._FlightROTEvadeFire,self,"Evading on enemy fire!") -local FlightMenuROTVertical=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Avoid radar and evade fire",FlightMenuROT,AI_ESCORT._FlightROTVertical,self,"Evading on enemy fire with vertical manoeuvres!") -end -return self -end -function AI_ESCORT:SetEscortMenuROT(EscortGroup) -for _,MenuROT in pairs(self.Menu.ROT)do -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -local EscortMenuROT=MENU_GROUP:New(self.PlayerGroup,"Reaction On Threat",EscortGroup.EscortMenu) -if not EscortGroup.EscortMenuEvasion then -if EscortGroup:OptionROTNoReactionPossible()then -local EscortMenuEvasionNoReaction=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Fight until death",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTNoReaction,"Fighting until death!") -end -if EscortGroup:OptionROTPassiveDefensePossible()then -local EscortMenuEvasionPassiveDefense=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Use flares, chaff and jammers",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTPassiveDefense,"Defending using jammers, chaff and flares!") -end -if EscortGroup:OptionROTEvadeFirePossible()then -local EscortMenuEvasionEvadeFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open fire",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTEvadeFire,"Evading on enemy fire!") -end -if EscortGroup:OptionROTVerticalPossible()then -local EscortMenuOptionEvasionVertical=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Avoid radar and evade fire",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTVertical,"Evading on enemy fire with vertical manoeuvres!") -end -end -end -end -return self -end -function AI_ESCORT:MenuROT(MenuTextFormat) -self:F(MenuTextFormat) -self.Menu.ROT=self.Menu.ROT or{} -self.Menu.ROT[#self.Menu.ROT+1]={} -return self -end -function AI_ESCORT:SetEscortMenuResumeMission(EscortGroup) -self:F() -if EscortGroup:IsAir()then -local EscortGroupName=EscortGroup:GetName() -EscortGroup.EscortMenuResumeMission=MENU_GROUP:New(self.PlayerGroup,"Resume from",EscortGroup.EscortMenu) -end -return self -end -function AI_ESCORT:_HoldPosition(OrbitGroup,EscortGroup,OrbitHeight,OrbitSeconds) -local EscortUnit=self.PlayerUnit -local OrbitUnit=OrbitGroup:GetUnit(1) -self:SetFlightModeMission(EscortGroup) -local PointFrom={} -local GroupVec3=EscortGroup:GetUnit(1):GetVec3() -PointFrom={} -PointFrom.x=GroupVec3.x -PointFrom.y=GroupVec3.z -PointFrom.speed=250 -PointFrom.type=AI.Task.WaypointType.TURNING_POINT -PointFrom.alt=GroupVec3.y -PointFrom.alt_type=AI.Task.AltitudeType.BARO -local OrbitPoint=OrbitUnit:GetVec2() -local PointTo={} -PointTo.x=OrbitPoint.x -PointTo.y=OrbitPoint.y -PointTo.speed=250 -PointTo.type=AI.Task.WaypointType.TURNING_POINT -PointTo.alt=OrbitHeight -PointTo.alt_type=AI.Task.AltitudeType.BARO -PointTo.task=EscortGroup:TaskOrbitCircleAtVec2(OrbitPoint,OrbitHeight,0) -local Points={PointFrom,PointTo} -EscortGroup:OptionROEHoldFire() -EscortGroup:OptionROTPassiveDefense() -EscortGroup:SetTask(EscortGroup:TaskRoute(Points),1) -EscortGroup:MessageTypeToGroup("Orbiting at current location.",MESSAGE.Type.Information,EscortUnit:GetGroup()) -end -function AI_ESCORT:_FlightHoldPosition(OrbitGroup,OrbitHeight,OrbitSeconds) -local EscortUnit=self.PlayerUnit -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup,OrbitGroup) -if EscortGroup:IsAir()then -if OrbitGroup==nil then -OrbitGroup=EscortGroup -end -self:_HoldPosition(OrbitGroup,EscortGroup,OrbitHeight,OrbitSeconds) -end -end,OrbitGroup -) -end -function AI_ESCORT:_JoinUp(EscortGroup) -local EscortUnit=self.PlayerUnit -self:SetFlightModeFormation(EscortGroup) -EscortGroup:MessageTypeToGroup("Joining up!",MESSAGE.Type.Information,EscortUnit:GetGroup()) -end -function AI_ESCORT:_FlightJoinUp() -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -if EscortGroup:IsAir()then -self:_JoinUp(EscortGroup) -end -end -) -end -function AI_ESCORT:_EscortFormationTrail(EscortGroup,XStart,XSpace,YStart) -self:FormationTrail(XStart,XSpace,YStart) -end -function AI_ESCORT:_FlightFormationTrail(XStart,XSpace,YStart) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -if EscortGroup:IsAir()then -self:_EscortFormationTrail(EscortGroup,XStart,XSpace,YStart) -end -end -) -end -function AI_ESCORT:_EscortFormationStack(EscortGroup,XStart,XSpace,YStart,YSpace) -self:FormationStack(XStart,XSpace,YStart,YSpace) -end -function AI_ESCORT:_FlightFormationStack(XStart,XSpace,YStart,YSpace) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -if EscortGroup:IsAir()then -self:_EscortFormationStack(EscortGroup,XStart,XSpace,YStart,YSpace) -end -end -) -end -function AI_ESCORT:_Flare(EscortGroup,Color,Message) -local EscortUnit=self.PlayerUnit -EscortGroup:GetUnit(1):Flare(Color) -EscortGroup:MessageTypeToGroup(Message,MESSAGE.Type.Information,EscortUnit:GetGroup()) -end -function AI_ESCORT:_FlightFlare(Color,Message) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -if EscortGroup:IsAir()then -self:_Flare(EscortGroup,Color,Message) -end -end -) -end -function AI_ESCORT:_Smoke(EscortGroup,Color,Message) -local EscortUnit=self.PlayerUnit -EscortGroup:GetUnit(1):Smoke(Color) -EscortGroup:MessageTypeToGroup(Message,MESSAGE.Type.Information,EscortUnit:GetGroup()) -end -function AI_ESCORT:_FlightSmoke(Color,Message) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -if EscortGroup:IsAir()then -self:_Smoke(EscortGroup,Color,Message) -end -end -) -end -function AI_ESCORT:_ReportNearbyTargetsNow(EscortGroup) -local EscortUnit=self.PlayerUnit -self:_ReportTargetsScheduler(EscortGroup) -end -function AI_ESCORT:_FlightReportNearbyTargetsNow() -self:_FlightReportTargetsScheduler() -end -function AI_ESCORT:_FlightSwitchReportNearbyTargets(ReportTargets) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -if EscortGroup:IsAir()then -self:_EscortSwitchReportNearbyTargets(EscortGroup,ReportTargets) -end -end -) -end -function AI_ESCORT:SetFlightReportType(ReportType) -self.FlightReportType=ReportType -end -function AI_ESCORT:GetFlightReportType() -return self.FlightReportType -end -function AI_ESCORT:_FlightSwitchReportTypeAll() -self:SetFlightReportType(self.__Enum.ReportType.All) -self:SetFlightMenuReportType() -local EscortGroup=self.EscortGroupSet:GetFirst() -EscortGroup:MessageTypeToGroup("Reporting all targets.",MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_FlightSwitchReportTypeAirborne() -self:SetFlightReportType(self.__Enum.ReportType.Airborne) -self:SetFlightMenuReportType() -local EscortGroup=self.EscortGroupSet:GetFirst() -EscortGroup:MessageTypeToGroup("Reporting airborne targets.",MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_FlightSwitchReportTypeGroundRadar() -self:SetFlightReportType(self.__Enum.ReportType.Ground) -self:SetFlightMenuReportType() -local EscortGroup=self.EscortGroupSet:GetFirst() -EscortGroup:MessageTypeToGroup("Reporting ground radar targets.",MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_FlightSwitchReportTypeGround() -self:SetFlightReportType(self.__Enum.ReportType.Ground) -self:SetFlightMenuReportType() -local EscortGroup=self.EscortGroupSet:GetFirst() -EscortGroup:MessageTypeToGroup("Reporting ground targets.",MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_ScanTargets(ScanDuration) -local EscortGroup=self.EscortGroup -local EscortUnit=self.PlayerUnit -self.FollowScheduler:Stop(self.FollowSchedule) -if EscortGroup:IsHelicopter()then -EscortGroup:PushTask( -EscortGroup:TaskControlled( -EscortGroup:TaskOrbitCircle(200,20), -EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil) -),1) -elseif EscortGroup:IsAirPlane()then -EscortGroup:PushTask( -EscortGroup:TaskControlled( -EscortGroup:TaskOrbitCircle(1000,500), -EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil) -),1) -end -EscortGroup:MessageToClient("Scanning targets for "..ScanDuration.." seconds.",ScanDuration,EscortUnit) -if self.EscortMode==AI_ESCORT.MODE.FOLLOW then -self.FollowScheduler:Start(self.FollowSchedule) -end -end -function AI_ESCORT.___Resume(EscortGroup,self) -self:F({self=self}) -local PlayerGroup=self.PlayerGroup -EscortGroup:OptionROEHoldFire() -EscortGroup:OptionROTVertical() -EscortGroup:SetState(EscortGroup,"Mode",EscortGroup:GetState(EscortGroup,"PreviousMode")) -if EscortGroup:GetState(EscortGroup,"Mode")==self.__Enum.Mode.Mission then -EscortGroup:MessageTypeToGroup("Resuming route.",MESSAGE.Type.Information,PlayerGroup) -else -EscortGroup:MessageTypeToGroup("Rejoining formation.",MESSAGE.Type.Information,PlayerGroup) -end -end -function AI_ESCORT:_ResumeMission(EscortGroup,WayPoint) -self:SetFlightModeMission(EscortGroup) -local WayPoints=EscortGroup.MissionRoute -self:T(WayPoint,WayPoints) -for WayPointIgnore=1,WayPoint do -table.remove(WayPoints,1) -end -EscortGroup:SetTask(EscortGroup:TaskRoute(WayPoints),1) -EscortGroup:MessageTypeToGroup("Resuming mission from waypoint ",MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_AttackTarget(EscortGroup,DetectedItem) -self:F(EscortGroup) -self:SetFlightModeAttack(EscortGroup) -if EscortGroup:IsAir()then -EscortGroup:OptionROEOpenFire() -EscortGroup:OptionROTVertical() -EscortGroup:SetState(EscortGroup,"Escort",self) -local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem) -local Tasks={} -local AttackUnitTasks={} -DetectedSet:ForEachUnit( -function(DetectedUnit,Tasks) -if DetectedUnit:IsAlive()then -AttackUnitTasks[#AttackUnitTasks+1]=EscortGroup:TaskAttackUnit(DetectedUnit) -end -end,Tasks -) -Tasks[#Tasks+1]=EscortGroup:TaskCombo(AttackUnitTasks) -Tasks[#Tasks+1]=EscortGroup:TaskFunction("AI_ESCORT.___Resume",self) -EscortGroup:PushTask( -EscortGroup:TaskCombo( -Tasks -),1 -) -else -local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem) -local Tasks={} -DetectedSet:ForEachUnit( -function(DetectedUnit,Tasks) -if DetectedUnit:IsAlive()then -Tasks[#Tasks+1]=EscortGroup:TaskFireAtPoint(DetectedUnit:GetVec2(),50) -end -end,Tasks -) -EscortGroup:PushTask( -EscortGroup:TaskCombo( -Tasks -),1 -) -end -local DetectedTargetsReport=REPORT:New("Engaging target:\n") -local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,self.PlayerGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName())) -local ReportSummary=DetectedItemReportSummary:Text(", ") -DetectedTargetsReport:AddIndent(ReportSummary,"-") -EscortGroup:MessageTypeToGroup(DetectedTargetsReport:Text(),MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_FlightAttackTarget(DetectedItem) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup,DetectedItem) -if EscortGroup:IsAir()then -self:_AttackTarget(EscortGroup,DetectedItem) -end -end,DetectedItem -) -end -function AI_ESCORT:_FlightAttackNearestTarget(TargetType) -self.Detection:Detect() -self:_FlightReportTargetsScheduler() -local EscortGroup=self.EscortGroupSet:GetFirst() -local AttackDetectedItem=nil -local DetectedItems=self.Detection:GetDetectedItems() -for DetectedItemIndex,DetectedItem in UTILS.spairs(DetectedItems,function(t,a,b)return self:Distance(self.PlayerUnit,t[a])0 -local HasAir=DetectedItemSet:HasAirUnits()>0 -local FlightReportType=self:GetFlightReportType() -if(TargetType and TargetType==self.__Enum.ReportType.Ground and HasGround)or -(TargetType and TargetType==self.__Enum.ReportType.Air and HasAir)or -(TargetType==nil)then -AttackDetectedItem=DetectedItem -break -end -end -if AttackDetectedItem then -self:_FlightAttackTarget(AttackDetectedItem) -else -EscortGroup:MessageTypeToGroup("Nothing to attack!",MESSAGE.Type.Information,self.PlayerGroup) -end -end -function AI_ESCORT:_AssistTarget(EscortGroup,DetectedItem) -local EscortUnit=self.PlayerUnit -local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem) -local Tasks={} -DetectedSet:ForEachUnit( -function(DetectedUnit,Tasks) -if DetectedUnit:IsAlive()then -Tasks[#Tasks+1]=EscortGroup:TaskFireAtPoint(DetectedUnit:GetVec2(),50) -end -end,Tasks -) -EscortGroup:SetTask( -EscortGroup:TaskCombo( -Tasks -),1 -) -EscortGroup:MessageTypeToGroup("Assisting attack!",MESSAGE.Type.Information,EscortUnit:GetGroup()) -end -function AI_ESCORT:_ROE(EscortGroup,EscortROEFunction,EscortROEMessage) -pcall(function()EscortROEFunction(EscortGroup)end) -EscortGroup:MessageTypeToGroup(EscortROEMessage,MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_FlightROEHoldFire(EscortROEMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROE(EscortGroup,EscortGroup.OptionROEHoldFire,EscortROEMessage) -end -) -end -function AI_ESCORT:_FlightROEOpenFire(EscortROEMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROE(EscortGroup,EscortGroup.OptionROEOpenFire,EscortROEMessage) -end -) -end -function AI_ESCORT:_FlightROEReturnFire(EscortROEMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROE(EscortGroup,EscortGroup.OptionROEReturnFire,EscortROEMessage) -end -) -end -function AI_ESCORT:_FlightROEWeaponFree(EscortROEMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROE(EscortGroup,EscortGroup.OptionROEWeaponFree,EscortROEMessage) -end -) -end -function AI_ESCORT:_ROT(EscortGroup,EscortROTFunction,EscortROTMessage) -pcall(function()EscortROTFunction(EscortGroup)end) -EscortGroup:MessageTypeToGroup(EscortROTMessage,MESSAGE.Type.Information,self.PlayerGroup) -end -function AI_ESCORT:_FlightROTNoReaction(EscortROTMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROT(EscortGroup,EscortGroup.OptionROTNoReaction,EscortROTMessage) -end -) -end -function AI_ESCORT:_FlightROTPassiveDefense(EscortROTMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROT(EscortGroup,EscortGroup.OptionROTPassiveDefense,EscortROTMessage) -end -) -end -function AI_ESCORT:_FlightROTEvadeFire(EscortROTMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROT(EscortGroup,EscortGroup.OptionROTEvadeFire,EscortROTMessage) -end -) -end -function AI_ESCORT:_FlightROTVertical(EscortROTMessage) -self.EscortGroupSet:ForEachGroupAlive( -function(EscortGroup) -self:_ROT(EscortGroup,EscortGroup.OptionROTVertical,EscortROTMessage) -end -) -end -function AI_ESCORT:RegisterRoute() -self:F() -local EscortGroup=self.EscortGroup -local TaskPoints=EscortGroup:GetTaskRoute() -self:T(TaskPoints) -return TaskPoints -end -function AI_ESCORT:_ResumeScheduler(EscortGroup) -self:F(EscortGroup:GetName()) -if EscortGroup:IsAlive()and self.PlayerUnit:IsAlive()then -local EscortGroupName=EscortGroup:GetCallsign() -if EscortGroup.EscortMenuResumeMission then -EscortGroup.EscortMenuResumeMission:RemoveSubMenus() -local TaskPoints=EscortGroup.MissionRoute -for WayPointID,WayPoint in pairs(TaskPoints)do -local EscortVec3=EscortGroup:GetVec3() -local Distance=((WayPoint.x-EscortVec3.x)^2+ -(WayPoint.y-EscortVec3.z)^2 -)^0.5/1000 -MENU_GROUP_COMMAND:New(self.PlayerGroup,"Waypoint "..WayPointID.." at "..string.format("%.2f",Distance).."km",EscortGroup.EscortMenuResumeMission,AI_ESCORT._ResumeMission,self,EscortGroup,WayPointID) -end -end -end -end -function AI_ESCORT:Distance(PlayerUnit,DetectedItem) -local DetectedCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem) -local PlayerCoordinate=PlayerUnit:GetCoordinate() -return DetectedCoordinate:Get3DDistance(PlayerCoordinate) -end -function AI_ESCORT:_ReportTargetsScheduler(EscortGroup,Report) -self:F(EscortGroup:GetName()) -if EscortGroup:IsAlive()and self.PlayerUnit:IsAlive()then -local EscortGroupName=EscortGroup:GetCallsign() -local DetectedTargetsReport=REPORT:New("Reporting targets:\n") -if EscortGroup.EscortMenuTargetAssistance then -EscortGroup.EscortMenuTargetAssistance:RemoveSubMenus() -end -local DetectedItems=self.Detection:GetDetectedItems() -local ClientEscortTargets=self.Detection -local TimeUpdate=timer.getTime() -local EscortMenuAttackTargets=MENU_GROUP:New(self.PlayerGroup,"Attack targets",EscortGroup.EscortMenu) -local DetectedTargets=false -for DetectedItemIndex,DetectedItem in UTILS.spairs(DetectedItems,function(t,a,b)return self:Distance(self.PlayerUnit,t[a])0 -local HasGroundRadar=HasGround and DetectedItemSet:HasRadar()>0 -local HasAir=DetectedItemSet:HasAirUnits()>0 -local FlightReportType=self:GetFlightReportType() -if(FlightReportType==self.__Enum.ReportType.All)or -(FlightReportType==self.__Enum.ReportType.Airborne and HasAir)or -(FlightReportType==self.__Enum.ReportType.Ground and HasGround)or -(FlightReportType==self.__Enum.ReportType.GroundRadar and HasGroundRadar)then -DetectedTargets=true -local DetectedMenu=self.Detection:DetectedItemReportMenu(DetectedItem,EscortGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName())):Text("\n") -local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,EscortGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName())) -local ReportSummary=DetectedItemReportSummary:Text(", ") -DetectedTargetsReport:AddIndent(ReportSummary,"-") -if EscortGroup:IsAir()then -MENU_GROUP_COMMAND:New(self.PlayerGroup, -DetectedMenu, -EscortMenuAttackTargets, -AI_ESCORT._AttackTarget, -self, -EscortGroup, -DetectedItem -):SetTag("Escort"):SetTime(TimeUpdate) -else -if self.EscortMenuTargetAssistance then -local MenuTargetAssistance=MENU_GROUP:New(self.PlayerGroup,EscortGroupName,EscortGroup.EscortMenuTargetAssistance) -MENU_GROUP_COMMAND:New(self.PlayerGroup, -DetectedMenu, -MenuTargetAssistance, -AI_ESCORT._AssistTarget, -self, -EscortGroup, -DetectedItem -) -end -end -end -end -EscortMenuAttackTargets:RemoveSubMenus(TimeUpdate,"Escort") -if Report then -if DetectedTargets then -EscortGroup:MessageTypeToGroup(DetectedTargetsReport:Text("\n"),MESSAGE.Type.Information,self.PlayerGroup) -else -EscortGroup:MessageTypeToGroup("No targets detected.",MESSAGE.Type.Information,self.PlayerGroup) -end -end -return true -end -return false -end -function AI_ESCORT:_FlightReportTargetsScheduler() -self:F("FlightReportTargetScheduler") -local EscortGroup=self.EscortGroupSet:GetFirst() -local DetectedTargetsReport=REPORT:New("Reporting your targets:\n") -if EscortGroup and(self.PlayerUnit:IsAlive()and EscortGroup:IsAlive())then -local TimeUpdate=timer.getTime() -local DetectedItems=self.Detection:GetDetectedItems() -local DetectedTargets=false -local ClientEscortTargets=self.Detection -for DetectedItemIndex,DetectedItem in UTILS.spairs(DetectedItems,function(t,a,b)return self:Distance(self.PlayerUnit,t[a])0 -local HasGroundRadar=HasGround and DetectedItemSet:HasRadar()>0 -local HasAir=DetectedItemSet:HasAirUnits()>0 -local FlightReportType=self:GetFlightReportType() -if(FlightReportType==self.__Enum.ReportType.All)or -(FlightReportType==self.__Enum.ReportType.Airborne and HasAir)or -(FlightReportType==self.__Enum.ReportType.Ground and HasGround)or -(FlightReportType==self.__Enum.ReportType.GroundRadar and HasGroundRadar)then -DetectedTargets=true -local DetectedItemReportMenu=self.Detection:DetectedItemReportMenu(DetectedItem,self.PlayerGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName())) -local ReportMenuText=DetectedItemReportMenu:Text(", ") -MENU_GROUP_COMMAND:New(self.PlayerGroup, -ReportMenuText, -self.FlightMenuAttack, -AI_ESCORT._FlightAttackTarget, -self, -DetectedItem -):SetTag("Flight"):SetTime(TimeUpdate) -local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,self.PlayerGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName())) -local ReportSummary=DetectedItemReportSummary:Text(", ") -DetectedTargetsReport:AddIndent(ReportSummary,"-") -end -end -self.FlightMenuAttack:RemoveSubMenus(TimeUpdate,"Flight") -if DetectedTargets then -EscortGroup:MessageTypeToGroup(DetectedTargetsReport:Text("\n"),MESSAGE.Type.Information,self.PlayerGroup) -end -return true -end -return false -end -AI_ESCORT_REQUEST={ -ClassName="AI_ESCORT_REQUEST", -} -function AI_ESCORT_REQUEST:New(EscortUnit,EscortSpawn,EscortAirbase,EscortName,EscortBriefing) -local EscortGroupSet=SET_GROUP:New():FilterDeads():FilterCrashes() -local self=BASE:Inherit(self,AI_ESCORT:New(EscortUnit,EscortGroupSet,EscortName,EscortBriefing)) -self.EscortGroupSet=EscortGroupSet -self.EscortSpawn=EscortSpawn -self.EscortAirbase=EscortAirbase -self.LeaderGroup=self.PlayerUnit:GetGroup() -self.Detection=DETECTION_AREAS:New(self.EscortGroupSet,5000) -self.Detection:__Start(30) -self.SpawnMode=self.__Enum.Mode.Mission -return self -end -function AI_ESCORT_REQUEST:SpawnEscort() -local EscortGroup=self.EscortSpawn:SpawnAtAirbase(self.EscortAirbase,SPAWN.Takeoff.Hot) -self:ScheduleOnce(0.1, -function(EscortGroup) -EscortGroup:OptionROTVertical() -EscortGroup:OptionROEHoldFire() -self.EscortGroupSet:AddGroup(EscortGroup) -local LeaderEscort=self.EscortGroupSet:GetFirst() -local Report=REPORT:New() -Report:Add("Joining Up "..self.EscortGroupSet:GetUnitTypeNames():Text(", ").." from "..LeaderEscort:GetCoordinate():ToString(self.EscortUnit)) -LeaderEscort:MessageTypeToGroup(Report:Text(),MESSAGE.Type.Information,self.PlayerUnit) -self:SetFlightModeFormation(EscortGroup) -self:FormationTrail() -self:_InitFlightMenus() -self:_InitEscortMenus(EscortGroup) -self:_InitEscortRoute(EscortGroup) -function EscortGroup:OnEventDeadOrCrash(EventData) -self:F({"EventDead",EventData}) -self.EscortMenu:Remove() -end -EscortGroup:HandleEvent(EVENTS.Dead,EscortGroup.OnEventDeadOrCrash) -EscortGroup:HandleEvent(EVENTS.Crash,EscortGroup.OnEventDeadOrCrash) -end,EscortGroup -) -end -function AI_ESCORT_REQUEST:onafterStart(EscortGroupSet) -self:F() -if not self.MenuRequestEscort then -self.MainMenu=MENU_GROUP:New(self.PlayerGroup,self.EscortName) -self.MenuRequestEscort=MENU_GROUP_COMMAND:New(self.LeaderGroup,"Request new escort ",self.MainMenu, -function() -self:SpawnEscort() -end -) -end -self:GetParent(self).onafterStart(self,EscortGroupSet) -self:HandleEvent(EVENTS.Dead,self.OnEventDeadOrCrash) -self:HandleEvent(EVENTS.Crash,self.OnEventDeadOrCrash) -end -function AI_ESCORT_REQUEST:onafterStop(EscortGroupSet) -self:F() -EscortGroupSet:ForEachGroup( -function(EscortGroup) -EscortGroup:WayPointInitialize() -EscortGroup:OptionROTVertical() -EscortGroup:OptionROEOpenFire() -end -) -self.Detection:Stop() -self.MainMenu:Remove() -end -function AI_ESCORT_REQUEST:SetEscortSpawnMission() -self.SpawnMode=self.__Enum.Mode.Mission -end -AI_ESCORT_DISPATCHER={ -ClassName="AI_ESCORT_DISPATCHER", -} -AI_ESCORT_DISPATCHER.AI_Escorts={} -function AI_ESCORT_DISPATCHER:New(CarrierSet,EscortSpawn,EscortAirbase,EscortName,EscortBriefing) -local self=BASE:Inherit(self,FSM:New()) -self.CarrierSet=CarrierSet -self.EscortSpawn=EscortSpawn -self.EscortAirbase=EscortAirbase -self.EscortName=EscortName -self.EscortBriefing=EscortBriefing -self:SetStartState("Idle") -self:AddTransition("Monitoring","Monitor","Monitoring") -self:AddTransition("Idle","Start","Monitoring") -self:AddTransition("Monitoring","Stop","Idle") -function self.CarrierSet.OnAfterRemoved(CarrierSet,From,Event,To,CarrierName,Carrier) -self:F({Carrier=Carrier:GetName()}) -end -return self -end -function AI_ESCORT_DISPATCHER:onafterStart(From,Event,To) -self:HandleEvent(EVENTS.Birth) -self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventExit) -self:HandleEvent(EVENTS.Crash,self.OnEventExit) -self:HandleEvent(EVENTS.Dead,self.OnEventExit) -end -function AI_ESCORT_DISPATCHER:OnEventExit(EventData) -local PlayerGroupName=EventData.IniGroupName -local PlayerGroup=EventData.IniGroup -local PlayerUnit=EventData.IniUnit -self:I({EscortAirbase=self.EscortAirbase}) -self:I({PlayerGroupName=PlayerGroupName}) -self:I({PlayerGroup=PlayerGroup}) -self:I({FirstGroup=self.CarrierSet:GetFirst()}) -self:I({FindGroup=self.CarrierSet:FindGroup(PlayerGroupName)}) -if self.CarrierSet:FindGroup(PlayerGroupName)then -if self.AI_Escorts[PlayerGroupName]then -self.AI_Escorts[PlayerGroupName]:Stop() -self.AI_Escorts[PlayerGroupName]=nil -end -end -end -function AI_ESCORT_DISPATCHER:OnEventBirth(EventData) -local PlayerGroupName=EventData.IniGroupName -local PlayerGroup=EventData.IniGroup -local PlayerUnit=EventData.IniUnit -self:I({EscortAirbase=self.EscortAirbase}) -self:I({PlayerGroupName=PlayerGroupName}) -self:I({PlayerGroup=PlayerGroup}) -self:I({FirstGroup=self.CarrierSet:GetFirst()}) -self:I({FindGroup=self.CarrierSet:FindGroup(PlayerGroupName)}) -if self.CarrierSet:FindGroup(PlayerGroupName)then -if not self.AI_Escorts[PlayerGroupName]then -local LeaderUnit=PlayerUnit -local EscortGroup=self.EscortSpawn:SpawnAtAirbase(self.EscortAirbase,SPAWN.Takeoff.Hot) -self:I({EscortGroup=EscortGroup}) -self:ScheduleOnce(1, -function(EscortGroup) -local EscortSet=SET_GROUP:New() -EscortSet:AddGroup(EscortGroup) -self.AI_Escorts[PlayerGroupName]=AI_ESCORT:New(LeaderUnit,EscortSet,self.EscortName,self.EscortBriefing) -self.AI_Escorts[PlayerGroupName]:FormationTrail(0,100,0) -if EscortGroup:IsHelicopter()then -self.AI_Escorts[PlayerGroupName]:MenusHelicopters() -else -self.AI_Escorts[PlayerGroupName]:MenusAirplanes() -end -self.AI_Escorts[PlayerGroupName]:__Start(0.1) -end,EscortGroup -) -end -end -end -AI_ESCORT_DISPATCHER_REQUEST={ -ClassName="AI_ESCORT_DISPATCHER_REQUEST", -} -AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts={} -function AI_ESCORT_DISPATCHER_REQUEST:New(CarrierSet,EscortSpawn,EscortAirbase,EscortName,EscortBriefing) -local self=BASE:Inherit(self,FSM:New()) -self.CarrierSet=CarrierSet -self.EscortSpawn=EscortSpawn -self.EscortAirbase=EscortAirbase -self.EscortName=EscortName -self.EscortBriefing=EscortBriefing -self:SetStartState("Idle") -self:AddTransition("Monitoring","Monitor","Monitoring") -self:AddTransition("Idle","Start","Monitoring") -self:AddTransition("Monitoring","Stop","Idle") -function self.CarrierSet.OnAfterRemoved(CarrierSet,From,Event,To,CarrierName,Carrier) -self:F({Carrier=Carrier:GetName()}) -end -return self -end -function AI_ESCORT_DISPATCHER_REQUEST:onafterStart(From,Event,To) -self:HandleEvent(EVENTS.Birth) -self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventExit) -self:HandleEvent(EVENTS.Crash,self.OnEventExit) -self:HandleEvent(EVENTS.Dead,self.OnEventExit) -end -function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit(EventData) -local PlayerGroupName=EventData.IniGroupName -local PlayerGroup=EventData.IniGroup -local PlayerUnit=EventData.IniUnit -if self.CarrierSet:FindGroup(PlayerGroupName)then -if self.AI_Escorts[PlayerGroupName]then -self.AI_Escorts[PlayerGroupName]:Stop() -self.AI_Escorts[PlayerGroupName]=nil -end -end -end -function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth(EventData) -local PlayerGroupName=EventData.IniGroupName -local PlayerGroup=EventData.IniGroup -local PlayerUnit=EventData.IniUnit -if self.CarrierSet:FindGroup(PlayerGroupName)then -if not self.AI_Escorts[PlayerGroupName]then -local LeaderUnit=PlayerUnit -self:ScheduleOnce(0.1, -function() -self.AI_Escorts[PlayerGroupName]=AI_ESCORT_REQUEST:New(LeaderUnit,self.EscortSpawn,self.EscortAirbase,self.EscortName,self.EscortBriefing) -self.AI_Escorts[PlayerGroupName]:FormationTrail(0,100,0) -if PlayerGroup:IsHelicopter()then -self.AI_Escorts[PlayerGroupName]:MenusHelicopters() -else -self.AI_Escorts[PlayerGroupName]:MenusAirplanes() -end -self.AI_Escorts[PlayerGroupName]:__Start(0.1) -end -) -end -end -end -AI_CARGO={ -ClassName="AI_CARGO", -Coordinate=nil, -Carrier_Cargo={}, -} -function AI_CARGO:New(Carrier,CargoSet) -local self=BASE:Inherit(self,FSM_CONTROLLABLE:New(Carrier)) -self.CargoSet=CargoSet -self.CargoCarrier=Carrier -self:SetStartState("Unloaded") -self:AddTransition("Unloaded","Pickup","*") -self:AddTransition("Loaded","Deploy","*") -self:AddTransition("*","Load","Boarding") -self:AddTransition("Boarding","Board","Boarding") -self:AddTransition("Loaded","Board","Loaded") -self:AddTransition("Boarding","Loaded","Boarding") -self:AddTransition("Boarding","PickedUp","Loaded") -self:AddTransition("Loaded","Unload","Unboarding") -self:AddTransition("Unboarding","Unboard","Unboarding") -self:AddTransition("Unboarding","Unloaded","Unboarding") -self:AddTransition("Unboarding","Deployed","Unloaded") -for _,CarrierUnit in pairs(Carrier:GetUnits())do -local CarrierUnit=CarrierUnit -CarrierUnit:SetCargoBayWeightLimit() -end -self.Transporting=false -self.Relocating=false -return self -end -function AI_CARGO:IsTransporting() -return self.Transporting==true -end -function AI_CARGO:IsRelocating() -return self.Relocating==true -end -function AI_CARGO:onafterPickup(APC,From,Event,To,Coordinate,Speed,Height,PickupZone) -self.Transporting=false -self.Relocating=true -end -function AI_CARGO:onafterDeploy(APC,From,Event,To,Coordinate,Speed,Height,DeployZone) -self.Relocating=false -self.Transporting=true -end -function AI_CARGO:onbeforeLoad(Carrier,From,Event,To,PickupZone) -self:F({Carrier,From,Event,To}) -local Boarding=false -local LoadInterval=2 -local LoadDelay=1 -local Carrier_List={} -local Carrier_Weight={} -if Carrier and Carrier:IsAlive()then -self.Carrier_Cargo={} -for _,CarrierUnit in pairs(Carrier:GetUnits())do -local CarrierUnit=CarrierUnit -local CargoBayFreeWeight=CarrierUnit:GetCargoBayFreeWeight() -self:F({CargoBayFreeWeight=CargoBayFreeWeight}) -Carrier_List[#Carrier_List+1]=CarrierUnit -Carrier_Weight[CarrierUnit]=CargoBayFreeWeight -end -local Carrier_Count=#Carrier_List -local Carrier_Index=1 -local Loaded=false -for _,Cargo in UTILS.spairs(self.CargoSet:GetSet(),function(t,a,b)return t[a]:GetWeight()>t[b]:GetWeight()end)do -local Cargo=Cargo -self:F({IsUnLoaded=Cargo:IsUnLoaded(),IsDeployed=Cargo:IsDeployed(),Cargo:GetName(),Carrier:GetName()}) -for Carrier_Loop=1,#Carrier_List do -local CarrierUnit=Carrier_List[Carrier_Index] -Carrier_Index=Carrier_Index+1 -if Carrier_Index>Carrier_Count then -Carrier_Index=1 -end -if Cargo:IsUnLoaded()and not Cargo:IsDeployed()then -if Cargo:IsInLoadRadius(CarrierUnit:GetCoordinate())then -self:F({"In radius",CarrierUnit:GetName()}) -local CargoWeight=Cargo:GetWeight() -local CarrierSpace=Carrier_Weight[CarrierUnit] -if CarrierSpace>CargoWeight then -Carrier:RouteStop() -Cargo:__Board(-LoadDelay,CarrierUnit) -self:__Board(LoadDelay,Cargo,CarrierUnit,PickupZone) -LoadDelay=LoadDelay+Cargo:GetCount()*LoadInterval -self.Carrier_Cargo[Cargo]=CarrierUnit -Boarding=true -Carrier_Weight[CarrierUnit]=Carrier_Weight[CarrierUnit]-CargoWeight -Loaded=true -break -else -self:T(string.format("WARNING: Cargo too heavy for carrier %s. Cargo=%.1f > %.1f free space",tostring(CarrierUnit:GetName()),CargoWeight,CarrierSpace)) -end -end -end -end -end -if not Loaded==true then -self.Relocating=false -end -end -return Boarding -end -function AI_CARGO:onbeforeReload(Carrier,From,Event,To) -self:F({Carrier,From,Event,To}) -local Boarding=false -local LoadInterval=2 -local LoadDelay=1 -local Carrier_List={} -local Carrier_Weight={} -if Carrier and Carrier:IsAlive()then -for _,CarrierUnit in pairs(Carrier:GetUnits())do -local CarrierUnit=CarrierUnit -Carrier_List[#Carrier_List+1]=CarrierUnit -end -local Carrier_Count=#Carrier_List -local Carrier_Index=1 -local Loaded=false -for Cargo,CarrierUnit in pairs(self.Carrier_Cargo)do -local Cargo=Cargo -self:F({IsUnLoaded=Cargo:IsUnLoaded(),IsDeployed=Cargo:IsDeployed(),Cargo:GetName(),Carrier:GetName()}) -for Carrier_Loop=1,#Carrier_List do -local CarrierUnit=Carrier_List[Carrier_Index] -Carrier_Index=Carrier_Index+1 -if Carrier_Index>Carrier_Count then -Carrier_Index=1 -end -if Cargo:IsUnLoaded()and not Cargo:IsDeployed()then -Carrier:RouteStop() -Cargo:__Board(-LoadDelay,CarrierUnit) -self:__Board(LoadDelay,Cargo,CarrierUnit) -LoadDelay=LoadDelay+Cargo:GetCount()*LoadInterval -self.Carrier_Cargo[Cargo]=CarrierUnit -Boarding=true -Loaded=true -end -end -end -if not Loaded==true then -self.Relocating=false -end -end -return Boarding -end -function AI_CARGO:onafterBoard(Carrier,From,Event,To,Cargo,CarrierUnit,PickupZone) -self:F({Carrier,From,Event,To,Cargo,CarrierUnit:GetName()}) -if Carrier and Carrier:IsAlive()and From=="Boarding"then -self:F({IsLoaded=Cargo:IsLoaded(),Cargo:GetName(),Carrier:GetName()}) -if not Cargo:IsLoaded()and not Cargo:IsDestroyed()then -self:__Board(-10,Cargo,CarrierUnit,PickupZone) -return -end -end -self:__Loaded(0.1,Cargo,CarrierUnit,PickupZone) -end -function AI_CARGO:onafterLoaded(Carrier,From,Event,To,Cargo,PickupZone) -self:F({Carrier,From,Event,To}) -local Loaded=true -if Carrier and Carrier:IsAlive()then -for Cargo,CarrierUnit in pairs(self.Carrier_Cargo)do -local Cargo=Cargo -self:F({IsLoaded=Cargo:IsLoaded(),IsDestroyed=Cargo:IsDestroyed(),Cargo:GetName(),Carrier:GetName()}) -if not Cargo:IsLoaded()and not Cargo:IsDestroyed()then -Loaded=false -end -end -end -if Loaded then -self:__PickedUp(0.1,PickupZone) -end -end -function AI_CARGO:onafterPickedUp(Carrier,From,Event,To,PickupZone) -self:F({Carrier,From,Event,To}) -Carrier:RouteResume() -local HasCargo=false -if Carrier and Carrier:IsAlive()then -for Cargo,CarrierUnit in pairs(self.Carrier_Cargo)do -HasCargo=true -break -end -end -self.Relocating=false -if HasCargo then -self:F("Transporting") -self.Transporting=true -end -end -function AI_CARGO:onafterUnload(Carrier,From,Event,To,DeployZone,Defend) -self:F({Carrier,From,Event,To,DeployZone,Defend=Defend}) -local UnboardInterval=5 -local UnboardDelay=5 -if Carrier and Carrier:IsAlive()then -for _,CarrierUnit in pairs(Carrier:GetUnits())do -local CarrierUnit=CarrierUnit -Carrier:RouteStop() -for _,Cargo in pairs(CarrierUnit:GetCargo())do -self:F({Cargo=Cargo:GetName(),Isloaded=Cargo:IsLoaded()}) -if Cargo:IsLoaded()then -Cargo:__UnBoard(UnboardDelay) -UnboardDelay=UnboardDelay+Cargo:GetCount()*UnboardInterval -self:__Unboard(UnboardDelay,Cargo,CarrierUnit,DeployZone,Defend) -if not Defend==true then -Cargo:SetDeployed(true) -end -end -end -end -end -end -function AI_CARGO:onafterUnboard(Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend) -self:F({Carrier,From,Event,To,Cargo:GetName(),DeployZone=DeployZone,Defend=Defend}) -if Carrier and Carrier:IsAlive()and From=="Unboarding"then -if not Cargo:IsUnLoaded()then -self:__Unboard(10,Cargo,CarrierUnit,DeployZone,Defend) -return -end -end -self:Unloaded(Cargo,CarrierUnit,DeployZone,Defend) -end -function AI_CARGO:onafterUnloaded(Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend) -self:F({Carrier,From,Event,To,Cargo:GetName(),DeployZone=DeployZone,Defend=Defend}) -local AllUnloaded=true -if Carrier and Carrier:IsAlive()then -for _,CarrierUnit in pairs(Carrier:GetUnits())do -local CarrierUnit=CarrierUnit -local IsEmpty=CarrierUnit:IsCargoEmpty() -self:I({IsEmpty=IsEmpty}) -if not IsEmpty then -AllUnloaded=false -break -end -end -if AllUnloaded==true then -if DeployZone==true then -self.Carrier_Cargo={} -end -self.CargoCarrier=Carrier -end -end -if AllUnloaded==true then -self:__Deployed(5,DeployZone,Defend) -end -end -function AI_CARGO:onafterDeployed(Carrier,From,Event,To,DeployZone,Defend) -self:F({Carrier,From,Event,To,DeployZone=DeployZone,Defend=Defend}) -if not Defend==true then -self.Transporting=false -else -self:F("Defending") -end -end -AI_CARGO_APC={ -ClassName="AI_CARGO_APC", -Coordinate=nil, -} -function AI_CARGO_APC:New(APC,CargoSet,CombatRadius) -local self=BASE:Inherit(self,AI_CARGO:New(APC,CargoSet)) -self:AddTransition("*","Monitor","*") -self:AddTransition("*","Follow","Following") -self:AddTransition("*","Guard","Unloaded") -self:AddTransition("*","Home","*") -self:AddTransition("*","Reload","Boarding") -self:AddTransition("*","Destroyed","Destroyed") -self:SetCombatRadius(CombatRadius) -self:SetCarrier(APC) -return self -end -function AI_CARGO_APC:SetCarrier(CargoCarrier) -self.CargoCarrier=CargoCarrier -self.CargoCarrier:SetState(self.CargoCarrier,"AI_CARGO_APC",self) -CargoCarrier:HandleEvent(EVENTS.Dead) -function CargoCarrier:OnEventDead(EventData) -self:F({"dead"}) -local AICargoTroops=self:GetState(self,"AI_CARGO_APC") -self:F({AICargoTroops=AICargoTroops}) -if AICargoTroops then -self:F({}) -if not AICargoTroops:Is("Loaded")then -AICargoTroops:Destroyed() -end -end -end -self.Zone=ZONE_UNIT:New(self.CargoCarrier:GetName().."-Zone",self.CargoCarrier,self.CombatRadius) -self.Coalition=self.CargoCarrier:GetCoalition() -self:SetControllable(CargoCarrier) -self:Guard() -return self -end -function AI_CARGO_APC:SetOffRoad(Offroad,Formation) -self:SetPickupOffRoad(Offroad,Formation) -self:SetDeployOffRoad(Offroad,Formation) -return self -end -function AI_CARGO_APC:SetPickupOffRoad(Offroad,Formation) -self.pickupOffroad=Offroad -self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad -return self -end -function AI_CARGO_APC:SetDeployOffRoad(Offroad,Formation) -self.deployOffroad=Offroad -self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad -return self -end -function AI_CARGO_APC:FindCarrier(Coordinate,Radius) -local CoordinateZone=ZONE_RADIUS:New("Zone",Coordinate:GetVec2(),Radius) -CoordinateZone:Scan({Object.Category.UNIT}) -for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do -local NearUnit=UNIT:Find(DCSUnit) -self:F({NearUnit=NearUnit}) -if not NearUnit:GetState(NearUnit,"AI_CARGO_APC")then -local Attributes=NearUnit:GetDesc() -self:F({Desc=Attributes}) -if NearUnit:HasAttribute("Trucks")then -return NearUnit:GetGroup() -end -end -end -return nil -end -function AI_CARGO_APC:SetCombatRadius(CombatRadius) -self.CombatRadius=CombatRadius or 0 -if self.CombatRadius>0 then -self:__Monitor(-5) -end -return self -end -function AI_CARGO_APC:FollowToCarrier(Me,APCUnit,CargoGroup) -local InfantryGroup=CargoGroup:GetGroup() -self:F({self=self:GetClassNameAndID(),InfantryGroup=InfantryGroup:GetName()}) -if APCUnit:IsAlive()then -if InfantryGroup:IsPartlyInZone(ZONE_UNIT:New("Radius",APCUnit,25))then -Me:Guard() -else -self:F({InfantryGroup=InfantryGroup:GetName()}) -if InfantryGroup:IsAlive()then -self:F({InfantryGroup=InfantryGroup:GetName()}) -local Waypoints={} -local FromCoord=InfantryGroup:GetCoordinate() -local FromGround=FromCoord:WaypointGround(10,"Diamond") -self:F({FromGround=FromGround}) -table.insert(Waypoints,FromGround) -local ToCoord=APCUnit:GetCoordinate():GetRandomCoordinateInRadius(10,5) -local ToGround=ToCoord:WaypointGround(10,"Diamond") -self:F({ToGround=ToGround}) -table.insert(Waypoints,ToGround) -local TaskRoute=InfantryGroup:TaskFunction("AI_CARGO_APC.FollowToCarrier",Me,APCUnit,CargoGroup) -self:F({Waypoints=Waypoints}) -local Waypoint=Waypoints[#Waypoints] -InfantryGroup:SetTaskWaypoint(Waypoint,TaskRoute) -InfantryGroup:Route(Waypoints,1) -end -end -end -end -function AI_CARGO_APC:onafterMonitor(APC,From,Event,To) -self:F({APC,From,Event,To,IsTransporting=self:IsTransporting()}) -if self.CombatRadius>0 then -if APC and APC:IsAlive()then -if self.CarrierCoordinate then -if self:IsTransporting()==true then -local Coordinate=APC:GetCoordinate() -if self:Is("Unloaded")or self:Is("Loaded")then -self.Zone:Scan({Object.Category.UNIT}) -if self.Zone:IsAllInZoneOfCoalition(self.Coalition)then -if self:Is("Unloaded")then -self:Reload() -end -else -if self:Is("Loaded")then -self:__Unload(1,nil,true) -else -if self:Is("Unloaded")then -end -self:F("I am here"..self:GetCurrentState()) -if self:Is("Following")then -for Cargo,APCUnit in pairs(self.Carrier_Cargo)do -local Cargo=Cargo -local APCUnit=APCUnit -if Cargo:IsAlive()then -if not Cargo:IsNear(APCUnit,40)then -APCUnit:RouteStop() -self.CarrierStopped=true -else -if self.CarrierStopped then -if Cargo:IsNear(APCUnit,25)then -APCUnit:RouteResume() -self.CarrierStopped=nil -end -end -end -end -end -end -end -end -end -end -end -self.CarrierCoordinate=APC:GetCoordinate() -end -self:__Monitor(-5) -end -end -function AI_CARGO_APC:onafterFollow(APC,From,Event,To) -self:F({APC,From,Event,To}) -self:F("Follow") -if APC and APC:IsAlive()then -for Cargo,APCUnit in pairs(self.Carrier_Cargo)do -local Cargo=Cargo -if Cargo:IsUnLoaded()then -self:FollowToCarrier(self,APCUnit,Cargo) -APCUnit:RouteResume() -end -end -end -end -function AI_CARGO_APC._Pickup(APC,self,Coordinate,Speed,PickupZone) -APC:F({"AI_CARGO_APC._Pickup:",APC:GetName()}) -if APC:IsAlive()then -self:Load(PickupZone) -end -end -function AI_CARGO_APC._Deploy(APC,self,Coordinate,DeployZone) -APC:F({"AI_CARGO_APC._Deploy:",APC}) -if APC:IsAlive()then -self:Unload(DeployZone) -end -end -function AI_CARGO_APC:onafterPickup(APC,From,Event,To,Coordinate,Speed,Height,PickupZone) -if APC and APC:IsAlive()then -if Coordinate then -self.RoutePickup=true -local _speed=Speed or APC:GetSpeedMax()*0.5 -local Waypoints={} -if self.pickupOffroad then -Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed,self.pickupFormation) -Waypoints[2]=Coordinate:WaypointGround(_speed,self.pickupFormation,DCSTasks) -else -Waypoints=APC:TaskGroundOnRoad(Coordinate,_speed,ENUMS.Formation.Vehicle.OffRoad,true) -end -local TaskFunction=APC:TaskFunction("AI_CARGO_APC._Pickup",self,Coordinate,Speed,PickupZone) -local Waypoint=Waypoints[#Waypoints] -APC:SetTaskWaypoint(Waypoint,TaskFunction) -APC:Route(Waypoints,1) -else -AI_CARGO_APC._Pickup(APC,self,Coordinate,Speed,PickupZone) -end -self:GetParent(self,AI_CARGO_APC).onafterPickup(self,APC,From,Event,To,Coordinate,Speed,Height,PickupZone) -end -end -function AI_CARGO_APC:onafterDeploy(APC,From,Event,To,Coordinate,Speed,Height,DeployZone) -if APC and APC:IsAlive()then -self.RouteDeploy=true -local speedmax=APC:GetSpeedMax() -local _speed=Speed or speedmax*0.5 -_speed=math.min(_speed,speedmax) -local Waypoints={} -if self.deployOffroad then -Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed,self.deployFormation) -Waypoints[2]=Coordinate:WaypointGround(_speed,self.deployFormation,DCSTasks) -else -Waypoints=APC:TaskGroundOnRoad(Coordinate,_speed,ENUMS.Formation.Vehicle.OffRoad,true) -end -local TaskFunction=APC:TaskFunction("AI_CARGO_APC._Deploy",self,Coordinate,DeployZone) -local Waypoint=Waypoints[#Waypoints] -APC:SetTaskWaypoint(Waypoint,TaskFunction) -APC:Route(Waypoints,1) -self:GetParent(self,AI_CARGO_APC).onafterDeploy(self,APC,From,Event,To,Coordinate,Speed,Height,DeployZone) -end -end -function AI_CARGO_APC:onafterUnloaded(Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend) -self:F({Carrier,From,Event,To,DeployZone=DeployZone,Defend=Defend}) -self:GetParent(self,AI_CARGO_APC).onafterUnloaded(self,Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend) -if Defend==true then -self.Zone:Scan({Object.Category.UNIT}) -if not self.Zone:IsAllInZoneOfCoalition(self.Coalition)then -local AttackUnits=self.Zone:GetScannedUnits() -local Move={} -local CargoGroup=Cargo.CargoObject -Move[#Move+1]=CargoGroup:GetCoordinate():WaypointGround(70,"Custom") -for UnitId,AttackUnit in pairs(AttackUnits)do -local MooseUnit=UNIT:Find(AttackUnit) -if MooseUnit:GetCoalition()~=CargoGroup:GetCoalition()then -Move[#Move+1]=MooseUnit:GetCoordinate():WaypointGround(70,"Line abreast") -self:F({MooseUnit=MooseUnit:GetName(),CargoGroup=CargoGroup:GetName()}) -end -end -CargoGroup:RoutePush(Move,0.1) -end -end -end -function AI_CARGO_APC:onafterDeployed(APC,From,Event,To,DeployZone,Defend) -self:F({APC,From,Event,To,DeployZone=DeployZone,Defend=Defend}) -self:__Guard(0.1) -self:GetParent(self,AI_CARGO_APC).onafterDeployed(self,APC,From,Event,To,DeployZone,Defend) -end -function AI_CARGO_APC:onafterHome(APC,From,Event,To,Coordinate,Speed,Height,HomeZone) -if APC and APC:IsAlive()~=nil then -self.RouteHome=true -Speed=Speed or APC:GetSpeedMax()*0.5 -local Waypoints=APC:TaskGroundOnRoad(Coordinate,Speed,"Line abreast",true) -self:F({Waypoints=Waypoints}) -local Waypoint=Waypoints[#Waypoints] -APC:Route(Waypoints,1) -end -end -AI_CARGO_HELICOPTER={ -ClassName="AI_CARGO_HELICOPTER", -Coordinate=nil, -} -AI_CARGO_QUEUE={} -function AI_CARGO_HELICOPTER:New(Helicopter,CargoSet) -local self=BASE:Inherit(self,AI_CARGO:New(Helicopter,CargoSet)) -self.Zone=ZONE_GROUP:New(Helicopter:GetName(),Helicopter,300) -self:SetStartState("Unloaded") -self:AddTransition("Unloaded","Pickup","*") -self:AddTransition("Loaded","Deploy","*") -self:AddTransition("*","Loaded","Loaded") -self:AddTransition("Unboarding","Pickup","Unloaded") -self:AddTransition("Unloaded","Unboard","Unloaded") -self:AddTransition("Unloaded","Unloaded","Unloaded") -self:AddTransition("*","PickedUp","*") -self:AddTransition("*","Landed","*") -self:AddTransition("*","Queue","*") -self:AddTransition("*","Orbit","*") -self:AddTransition("*","Home","*") -self:AddTransition("*","Destroyed","Destroyed") -Helicopter:HandleEvent(EVENTS.Crash, -function(Helicopter,EventData) -AI_CARGO_QUEUE[Helicopter]=nil -end -) -Helicopter:HandleEvent(EVENTS.Land, -function(Helicopter,EventData) -self:ScheduleOnce(60, -function(Helicopter) -AI_CARGO_QUEUE[Helicopter]=nil -end,Helicopter -) -end -) -self:SetCarrier(Helicopter) -return self -end -function AI_CARGO_HELICOPTER:SetCarrier(Helicopter) -local AICargo=self -self.Helicopter=Helicopter -self.Helicopter:SetState(self.Helicopter,"AI_CARGO_HELICOPTER",self) -self.RoutePickup=false -self.RouteDeploy=false -Helicopter:HandleEvent(EVENTS.Dead) -Helicopter:HandleEvent(EVENTS.Hit) -Helicopter:HandleEvent(EVENTS.Land) -function Helicopter:OnEventDead(EventData) -local AICargoTroops=self:GetState(self,"AI_CARGO_HELICOPTER") -self:F({AICargoTroops=AICargoTroops}) -if AICargoTroops then -self:F({}) -if not AICargoTroops:Is("Loaded")then -AICargoTroops:Destroyed() -end -end -end -function Helicopter:OnEventLand(EventData) -AICargo:Landed() -end -self.Coalition=self.Helicopter:GetCoalition() -self:SetControllable(Helicopter) -return self -end -function AI_CARGO_HELICOPTER:onafterLanded(Helicopter,From,Event,To) -self:F({From,Event,To}) -Helicopter:F({Name=Helicopter:GetName()}) -if Helicopter and Helicopter:IsAlive()then -self:F({Helicopter:GetName(),Height=Helicopter:GetHeight(true),Velocity=Helicopter:GetVelocityKMH()}) -if self.RoutePickup==true then -if Helicopter:GetHeight(true)<=5.5 and Helicopter:GetVelocityKMH()<10 then -self:Load(self.PickupZone) -self.RoutePickup=false -end -end -if self.RouteDeploy==true then -if Helicopter:GetHeight(true)<=5.5 and Helicopter:GetVelocityKMH()<10 then -self:Unload(self.DeployZone) -self.RouteDeploy=false -end -end -end -end -function AI_CARGO_HELICOPTER:onafterQueue(Helicopter,From,Event,To,Coordinate,Speed,DeployZone) -self:F({From,Event,To,Coordinate,Speed,DeployZone}) -local HelicopterInZone=false -if Helicopter and Helicopter:IsAlive()==true then -local Distance=Coordinate:DistanceFromPointVec2(Helicopter:GetCoordinate()) -if Distance>2000 then -self:__Queue(-10,Coordinate,Speed,DeployZone) -else -local ZoneFree=true -for Helicopter,ZoneQueue in pairs(AI_CARGO_QUEUE)do -local ZoneQueue=ZoneQueue -if ZoneQueue:IsCoordinateInZone(Coordinate)then -ZoneFree=false -end -end -self:F({ZoneFree=ZoneFree}) -if ZoneFree==true then -local ZoneQueue=ZONE_RADIUS:New(Helicopter:GetName(),Coordinate:GetVec2(),100) -AI_CARGO_QUEUE[Helicopter]=ZoneQueue -local Route={} -local CoordinateTo=Coordinate -local landheight=CoordinateTo:GetLandHeight() -CoordinateTo.y=landheight+50 -local WaypointTo=CoordinateTo:WaypointAir( -"RADIO", -POINT_VEC3.RoutePointType.TurningPoint, -POINT_VEC3.RoutePointAction.TurningPoint, -50, -true -) -Route[#Route+1]=WaypointTo -local Tasks={} -Tasks[#Tasks+1]=Helicopter:TaskLandAtVec2(CoordinateTo:GetVec2()) -Route[#Route].task=Helicopter:TaskCombo(Tasks) -Route[#Route+1]=WaypointTo -Helicopter:Route(Route,0) -self.DeployZone=DeployZone -else -self:__Queue(-10,Coordinate,Speed,DeployZone) -end -end -else -AI_CARGO_QUEUE[Helicopter]=nil -end -end -function AI_CARGO_HELICOPTER:onafterOrbit(Helicopter,From,Event,To,Coordinate) -self:F({From,Event,To,Coordinate}) -if Helicopter and Helicopter:IsAlive()then -local Route={} -local CoordinateTo=Coordinate -local landheight=CoordinateTo:GetLandHeight() -CoordinateTo.y=landheight+50 -local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,50,true) -Route[#Route+1]=WaypointTo -local Tasks={} -Tasks[#Tasks+1]=Helicopter:TaskOrbitCircle(math.random(30,80),150,CoordinateTo:GetRandomCoordinateInRadius(800,500)) -Route[#Route].task=Helicopter:TaskCombo(Tasks) -Route[#Route+1]=WaypointTo -Helicopter:Route(Route,0) -end -end -function AI_CARGO_HELICOPTER:onafterDeployed(Helicopter,From,Event,To,DeployZone) -self:F({From,Event,To,DeployZone=DeployZone}) -self:Orbit(Helicopter:GetCoordinate(),50) -self:ScheduleOnce(30, -function(Helicopter) -AI_CARGO_QUEUE[Helicopter]=nil -end,Helicopter -) -self:GetParent(self,AI_CARGO_HELICOPTER).onafterDeployed(self,Helicopter,From,Event,To,DeployZone) -end -function AI_CARGO_HELICOPTER:onafterPickup(Helicopter,From,Event,To,Coordinate,Speed,Height,PickupZone) -self:F({Coordinate,Speed,Height,PickupZone}) -if Helicopter and Helicopter:IsAlive()~=nil then -Helicopter:Activate() -self.RoutePickup=true -Coordinate.y=Height -local _speed=Speed or Helicopter:GetSpeedMax()*0.5 -local Route={} -local CoordinateFrom=Helicopter:GetCoordinate() -local WaypointFrom=CoordinateFrom:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true) -local CoordinateTo=Coordinate -local landheight=CoordinateTo:GetLandHeight() -CoordinateTo.y=landheight+50 -local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true) -Route[#Route+1]=WaypointFrom -Route[#Route+1]=WaypointTo -Helicopter:WayPointInitialize(Route) -local Tasks={} -Tasks[#Tasks+1]=Helicopter:TaskLandAtVec2(CoordinateTo:GetVec2()) -Route[#Route].task=Helicopter:TaskCombo(Tasks) -Route[#Route+1]=WaypointTo -Helicopter:Route(Route,1) -self.PickupZone=PickupZone -self:GetParent(self,AI_CARGO_HELICOPTER).onafterPickup(self,Helicopter,From,Event,To,Coordinate,Speed,Height,PickupZone) -end -end -function AI_CARGO_HELICOPTER:_Deploy(AICargoHelicopter,Coordinate,DeployZone) -AICargoHelicopter:__Queue(-10,Coordinate,100,DeployZone) -end -function AI_CARGO_HELICOPTER:onafterDeploy(Helicopter,From,Event,To,Coordinate,Speed,Height,DeployZone) -self:F({From,Event,To,Coordinate,Speed,Height,DeployZone}) -if Helicopter and Helicopter:IsAlive()~=nil then -self.RouteDeploy=true -local Route={} -Coordinate.y=Height -local _speed=Speed or Helicopter:GetSpeedMax()*0.5 -local CoordinateFrom=Helicopter:GetCoordinate() -local WaypointFrom=CoordinateFrom:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true) -Route[#Route+1]=WaypointFrom -Route[#Route+1]=WaypointFrom -local CoordinateTo=Coordinate -local landheight=CoordinateTo:GetLandHeight() -CoordinateTo.y=landheight+50 -local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true) -Route[#Route+1]=WaypointTo -Route[#Route+1]=WaypointTo -Helicopter:WayPointInitialize(Route) -local Tasks={} -Tasks[#Tasks+1]=Helicopter:TaskFunction("AI_CARGO_HELICOPTER._Deploy",self,Coordinate,DeployZone) -Tasks[#Tasks+1]=Helicopter:TaskOrbitCircle(math.random(30,100),_speed,CoordinateTo:GetRandomCoordinateInRadius(800,500)) -Route[#Route].task=Helicopter:TaskCombo(Tasks) -Route[#Route+1]=WaypointTo -Helicopter:Route(Route,0) -self:GetParent(self,AI_CARGO_HELICOPTER).onafterDeploy(self,Helicopter,From,Event,To,Coordinate,Speed,Height,DeployZone) -end -end -function AI_CARGO_HELICOPTER:onafterHome(Helicopter,From,Event,To,Coordinate,Speed,Height,HomeZone) -self:F({From,Event,To,Coordinate,Speed,Height}) -if Helicopter and Helicopter:IsAlive()~=nil then -self.RouteHome=true -local Route={} -Height=Height or 50 -Speed=Speed or Helicopter:GetSpeedMax()*0.5 -local CoordinateFrom=Helicopter:GetCoordinate() -local WaypointFrom=CoordinateFrom:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,Speed,true) -Route[#Route+1]=WaypointFrom -local CoordinateTo=Coordinate -local landheight=CoordinateTo:GetLandHeight() -CoordinateTo.y=landheight+Height -local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,Speed,true) -Route[#Route+1]=WaypointTo -Helicopter:WayPointInitialize(Route) -local Tasks={} -Tasks[#Tasks+1]=Helicopter:TaskLandAtVec2(CoordinateTo:GetVec2()) -Route[#Route].task=Helicopter:TaskCombo(Tasks) -Route[#Route+1]=WaypointTo -Helicopter:Route(Route,0) -end -end -AI_CARGO_AIRPLANE={ -ClassName="AI_CARGO_AIRPLANE", -Coordinate=nil, -} -function AI_CARGO_AIRPLANE:New(Airplane,CargoSet) -local self=BASE:Inherit(self,AI_CARGO:New(Airplane,CargoSet)) -self:AddTransition("*","Landed","*") -self:AddTransition("*","Home","*") -self:AddTransition("*","Destroyed","Destroyed") -self:SetCarrier(Airplane) -return self -end -function AI_CARGO_AIRPLANE:SetCarrier(Airplane) -local AICargo=self -self.Airplane=Airplane -self.Airplane:SetState(self.Airplane,"AI_CARGO_AIRPLANE",self) -self.RoutePickup=false -self.RouteDeploy=false -Airplane:HandleEvent(EVENTS.Dead) -Airplane:HandleEvent(EVENTS.Hit) -Airplane:HandleEvent(EVENTS.EngineShutdown) -function Airplane:OnEventDead(EventData) -local AICargoTroops=self:GetState(self,"AI_CARGO_AIRPLANE") -self:F({AICargoTroops=AICargoTroops}) -if AICargoTroops then -self:F({}) -if not AICargoTroops:Is("Loaded")then -AICargoTroops:Destroyed() -end -end -end -function Airplane:OnEventHit(EventData) -local AICargoTroops=self:GetState(self,"AI_CARGO_AIRPLANE") -if AICargoTroops then -self:F({OnHitLoaded=AICargoTroops:Is("Loaded")}) -if AICargoTroops:Is("Loaded")or AICargoTroops:Is("Boarding")then -AICargoTroops:Unload() -end -end -end -function Airplane:OnEventEngineShutdown(EventData) -AICargo.Relocating=false -AICargo:Landed(self.Airplane) -end -self.Coalition=self.Airplane:GetCoalition() -self:SetControllable(Airplane) -return self -end -function AI_CARGO_AIRPLANE:FindCarrier(Coordinate,Radius) -local CoordinateZone=ZONE_RADIUS:New("Zone",Coordinate:GetVec2(),Radius) -CoordinateZone:Scan({Object.Category.UNIT}) -for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do -local NearUnit=UNIT:Find(DCSUnit) -self:F({NearUnit=NearUnit}) -if not NearUnit:GetState(NearUnit,"AI_CARGO_AIRPLANE")then -local Attributes=NearUnit:GetDesc() -self:F({Desc=Attributes}) -if NearUnit:HasAttribute("Trucks")then -self:SetCarrier(NearUnit) -break -end -end -end -end -function AI_CARGO_AIRPLANE:onafterLanded(Airplane,From,Event,To) -self:F({Airplane,From,Event,To}) -if Airplane and Airplane:IsAlive()~=nil then -if self.RoutePickup==true then -self:Load(self.PickupZone) -end -if self.RouteDeploy==true then -self:Unload() -self.RouteDeploy=false -end -end -end -function AI_CARGO_AIRPLANE:onafterPickup(Airplane,From,Event,To,Coordinate,Speed,Height,PickupZone) -if Airplane and Airplane:IsAlive()then -local airbasepickup=Coordinate:GetClosestAirbase() -self.PickupZone=PickupZone or ZONE_AIRBASE:New(airbasepickup:GetName()) -local ClosestAirbase,DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase() -if Airplane:InAir()then -self.Airbase=nil -else -self.Airbase=ClosestAirbase -end -local Airbase=self.PickupZone:GetAirbase() -local Dist=Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate()) -if Airplane:InAir()or Dist>500 then -self:Route(Airplane,Airbase,Speed,Height) -self.Airbase=Airbase -self.RoutePickup=true -else -self.RoutePickup=true -self:Landed() -end -self:GetParent(self,AI_CARGO_AIRPLANE).onafterPickup(self,Airplane,From,Event,To,Coordinate,Speed,Height,self.PickupZone) -end -end -function AI_CARGO_AIRPLANE:onafterDeploy(Airplane,From,Event,To,Coordinate,Speed,Height,DeployZone) -if Airplane and Airplane:IsAlive()~=nil then -local Airbase=Coordinate:GetClosestAirbase() -if DeployZone then -Airbase=DeployZone:GetAirbase() -end -if Airplane:IsAlive()==false then -Airplane:SetCommand({id='Start',params={}}) -end -self:Route(Airplane,Airbase,Speed,Height) -self.RouteDeploy=true -self.Airbase=Airbase -self:GetParent(self,AI_CARGO_AIRPLANE).onafterDeploy(self,Airplane,From,Event,To,Coordinate,Speed,Height,DeployZone) -end -end -function AI_CARGO_AIRPLANE:onafterUnload(Airplane,From,Event,To,DeployZone) -local UnboardInterval=10 -local UnboardDelay=10 -if Airplane and Airplane:IsAlive()then -for _,AirplaneUnit in pairs(Airplane:GetUnits())do -local Cargos=AirplaneUnit:GetCargo() -for CargoID,Cargo in pairs(Cargos)do -local Angle=180 -local CargoCarrierHeading=Airplane:GetHeading() -local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle) -self:T({CargoCarrierHeading,CargoDeployHeading}) -local CargoDeployCoordinate=Airplane:GetPointVec2():Translate(150,CargoDeployHeading) -Cargo:__UnBoard(UnboardDelay,CargoDeployCoordinate) -UnboardDelay=UnboardDelay+UnboardInterval -Cargo:SetDeployed(true) -self:__Unboard(UnboardDelay,Cargo,AirplaneUnit,DeployZone) -end -end -end -end -function AI_CARGO_AIRPLANE:Route(Airplane,Airbase,Speed,Height,Uncontrolled) -if Airplane and Airplane:IsAlive()then -local Takeoff=SPAWN.Takeoff.Cold -local Template=Airplane:GetTemplate() -if Template==nil then -return -end -local Points={} -local AirbasePointVec2=Airbase:GetPointVec2() -local ToWaypoint=AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO,"Land","Landing",Speed or Airplane:GetSpeedMax()*0.8,true,Airbase) -if self.Airbase then -Template.route.points[2]=ToWaypoint -Airplane:RespawnAtCurrentAirbase(Template,Takeoff,Uncontrolled) -else -local GroupPoint=Airplane:GetVec2() -local FromWaypoint={} -FromWaypoint.x=GroupPoint.x -FromWaypoint.y=GroupPoint.y -FromWaypoint.type="Turning Point" -FromWaypoint.action="Turning Point" -FromWaypoint.speed=Airplane:GetSpeedMax()*0.8 -Points[1]=FromWaypoint -Points[2]=ToWaypoint -local PointVec3=Airplane:GetPointVec3() -Template.x=PointVec3.x -Template.y=PointVec3.z -Template.route.points=Points -local GroupSpawned=Airplane:Respawn(Template) -end -end -end -function AI_CARGO_AIRPLANE:onafterHome(Airplane,From,Event,To,Coordinate,Speed,Height,HomeZone) -if Airplane and Airplane:IsAlive()then -self.RouteHome=true -local HomeBase=HomeZone:GetAirbase() -self.Airbase=HomeBase -self:Route(Airplane,HomeBase,Speed,Height) -end -end -AI_CARGO_SHIP={ -ClassName="AI_CARGO_SHIP", -Coordinate=nil -} -function AI_CARGO_SHIP:New(Ship,CargoSet,CombatRadius,ShippingLane) -local self=BASE:Inherit(self,AI_CARGO:New(Ship,CargoSet)) -self:AddTransition("*","Monitor","*") -self:AddTransition("*","Destroyed","Destroyed") -self:AddTransition("*","Home","*") -self:SetCombatRadius(0) -self:SetShippingLane(ShippingLane) -self:SetCarrier(Ship) -return self -end -function AI_CARGO_SHIP:SetCarrier(CargoCarrier) -self.CargoCarrier=CargoCarrier -self.CargoCarrier:SetState(self.CargoCarrier,"AI_CARGO_SHIP",self) -CargoCarrier:HandleEvent(EVENTS.Dead) -function CargoCarrier:OnEventDead(EventData) -self:F({"dead"}) -local AICargoTroops=self:GetState(self,"AI_CARGO_SHIP") -self:F({AICargoTroops=AICargoTroops}) -if AICargoTroops then -self:F({}) -if not AICargoTroops:Is("Loaded")then -AICargoTroops:Destroyed() -end -end -end -self.Zone=ZONE_UNIT:New(self.CargoCarrier:GetName().."-Zone",self.CargoCarrier,self.CombatRadius) -self.Coalition=self.CargoCarrier:GetCoalition() -self:SetControllable(CargoCarrier) -return self -end -function AI_CARGO_SHIP:FindCarrier(Coordinate,Radius) -local CoordinateZone=ZONE_RADIUS:New("Zone",Coordinate:GetVec2(),Radius) -CoordinateZone:Scan({Object.Category.UNIT}) -for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do -local NearUnit=UNIT:Find(DCSUnit) -self:F({NearUnit=NearUnit}) -if not NearUnit:GetState(NearUnit,"AI_CARGO_SHIP")then -local Attributes=NearUnit:GetDesc() -self:F({Desc=Attributes}) -if NearUnit:HasAttributes("Trucks")then -return NearUnit:GetGroup() -end -end -end -return nil -end -function AI_CARGO_SHIP:SetShippingLane(ShippingLane) -self.ShippingLane=ShippingLane -return self -end -function AI_CARGO_SHIP:SetCombatRadius(CombatRadius) -self.CombatRadius=CombatRadius or 0 -return self -end -function AI_CARGO_SHIP:FollowToCarrier(Me,ShipUnit,CargoGroup) -local InfantryGroup=CargoGroup:GetGroup() -self:F({self=self:GetClassNameAndID(),InfantryGroup=InfantryGroup:GetName()}) -if ShipUnit:IsAlive()then -if InfantryGroup:IsPartlyInZone(ZONE_UNIT:New("Radius",ShipUnit,1000))then -Me:Guard() -else -self:F({InfantryGroup=InfantryGroup:GetName()}) -if InfantryGroup:IsAlive()then -self:F({InfantryGroup=InfantryGroup:GetName()}) -local Waypoints={} -local FromCoord=InfantryGroup:GetCoordinate() -local FromGround=FromCoord:WaypointGround(10,"Diamond") -self:F({FromGround=FromGround}) -table.insert(Waypoints,FromGround) -local ToCoord=ShipUnit:GetCoordinate():GetRandomCoordinateInRadius(10,5) -local ToGround=ToCoord:WaypointGround(10,"Diamond") -self:F({ToGround=ToGround}) -table.insert(Waypoints,ToGround) -local TaskRoute=InfantryGroup:TaskFunction("AI_CARGO_SHIP.FollowToCarrier",Me,ShipUnit,CargoGroup) -self:F({Waypoints=Waypoints}) -local Waypoint=Waypoints[#Waypoints] -InfantryGroup:SetTaskWaypoint(Waypoint,TaskRoute) -InfantryGroup:Route(Waypoints,1) -end -end -end -end -function AI_CARGO_SHIP:onafterMonitor(Ship,From,Event,To) -self:F({Ship,From,Event,To,IsTransporting=self:IsTransporting()}) -if self.CombatRadius>0 then -if Ship and Ship:IsAlive()then -if self.CarrierCoordinate then -if self:IsTransporting()==true then -local Coordinate=Ship:GetCoordinate() -if self:Is("Unloaded")or self:Is("Loaded")then -self.Zone:Scan({Object.Category.UNIT}) -if self.Zone:IsAllInZoneOfCoalition(self.Coalition)then -if self:Is("Unloaded")then -self:Reload() -end -else -if self:Is("Loaded")then -self:__Unload(1,nil,true) -else -if self:Is("Unloaded")then -end -self:F("I am here"..self:GetCurrentState()) -if self:Is("Following")then -for Cargo,ShipUnit in pairs(self.Carrier_Cargo)do -local Cargo=Cargo -local ShipUnit=ShipUnit -if Cargo:IsAlive()then -if not Cargo:IsNear(ShipUnit,40)then -ShipUnit:RouteStop() -self.CarrierStopped=true -else -if self.CarrierStopped then -if Cargo:IsNear(ShipUnit,25)then -ShipUnit:RouteResume() -self.CarrierStopped=nil -end -end -end -end -end -end -end -end -end -end -end -self.CarrierCoordinate=Ship:GetCoordinate() -end -self:__Monitor(-5) -end -end -function AI_CARGO_SHIP._Pickup(Ship,self,Coordinate,Speed,PickupZone) -Ship:F({"AI_CARGO_Ship._Pickup:",Ship:GetName()}) -if Ship:IsAlive()then -self:Load(PickupZone) -end -end -function AI_CARGO_SHIP._Deploy(Ship,self,Coordinate,DeployZone) -Ship:F({"AI_CARGO_Ship._Deploy:",Ship}) -if Ship:IsAlive()then -self:Unload(DeployZone) -end -end -function AI_CARGO_SHIP:onafterPickup(Ship,From,Event,To,Coordinate,Speed,Height,PickupZone) -if Ship and Ship:IsAlive()then -AI_CARGO_SHIP._Pickup(Ship,self,Coordinate,Speed,PickupZone) -self:GetParent(self,AI_CARGO_SHIP).onafterPickup(self,Ship,From,Event,To,Coordinate,Speed,Height,PickupZone) -end -end -function AI_CARGO_SHIP:onafterDeploy(Ship,From,Event,To,Coordinate,Speed,Height,DeployZone) -if Ship and Ship:IsAlive()then -Speed=Speed or Ship:GetSpeedMax()*0.8 -local lane=self.ShippingLane -if lane then -local Waypoints={} -for i=1,#lane do -local coord=lane[i] -local Waypoint=coord:WaypointGround(_speed) -table.insert(Waypoints,Waypoint) -end -local TaskFunction=Ship:TaskFunction("AI_CARGO_SHIP._Deploy",self,Coordinate,DeployZone) -local Waypoint=Waypoints[#Waypoints] -Ship:SetTaskWaypoint(Waypoint,TaskFunction) -Ship:Route(Waypoints,1) -self:GetParent(self,AI_CARGO_SHIP).onafterDeploy(self,Ship,From,Event,To,Coordinate,Speed,Height,DeployZone) -else -self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!") -end -end -end -function AI_CARGO_SHIP:onafterUnload(Ship,From,Event,To,DeployZone,Defend) -self:F({Ship,From,Event,To,DeployZone,Defend=Defend}) -local UnboardInterval=5 -local UnboardDelay=5 -if Ship and Ship:IsAlive()then -for _,ShipUnit in pairs(Ship:GetUnits())do -local ShipUnit=ShipUnit -Ship:RouteStop() -for _,Cargo in pairs(ShipUnit:GetCargo())do -self:F({Cargo=Cargo:GetName(),Isloaded=Cargo:IsLoaded()}) -if Cargo:IsLoaded()then -local unboardCoord=DeployZone:GetRandomPointVec2() -Cargo:__UnBoard(UnboardDelay,unboardCoord,1000) -UnboardDelay=UnboardDelay+Cargo:GetCount()*UnboardInterval -self:__Unboard(UnboardDelay,Cargo,ShipUnit,DeployZone,Defend) -if not Defend==true then -Cargo:SetDeployed(true) -end -end -end -end -end -end -function AI_CARGO_SHIP:onafterHome(Ship,From,Event,To,Coordinate,Speed,Height,HomeZone) -if Ship and Ship:IsAlive()then -self.RouteHome=true -Speed=Speed or Ship:GetSpeedMax()*0.8 -local lane=self.ShippingLane -if lane then -local Waypoints={} -for i=#lane,1,-1 do -local coord=lane[i] -local Waypoint=coord:WaypointGround(_speed) -table.insert(Waypoints,Waypoint) -end -local Waypoint=Waypoints[#Waypoints] -Ship:Route(Waypoints,1) -else -self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!") -end -end -end -AI_CARGO_DISPATCHER={ -ClassName="AI_CARGO_DISPATCHER", -AI_Cargo={}, -PickupCargo={} -} -AI_CARGO_DISPATCHER.AI_Cargo={} -AI_CARGO_DISPATCHER.PickupCargo={} -function AI_CARGO_DISPATCHER:New(CarrierSet,CargoSet,PickupZoneSet,DeployZoneSet) -local self=BASE:Inherit(self,FSM:New()) -self.SetCarrier=CarrierSet -self.SetCargo=CargoSet -self.PickupZoneSet=PickupZoneSet -self.DeployZoneSet=DeployZoneSet -self:SetStartState("Idle") -self:AddTransition("Monitoring","Monitor","Monitoring") -self:AddTransition("Idle","Start","Monitoring") -self:AddTransition("Monitoring","Stop","Idle") -self:AddTransition("Monitoring","Pickup","Monitoring") -self:AddTransition("Monitoring","Load","Monitoring") -self:AddTransition("Monitoring","Loading","Monitoring") -self:AddTransition("Monitoring","Loaded","Monitoring") -self:AddTransition("Monitoring","PickedUp","Monitoring") -self:AddTransition("Monitoring","Transport","Monitoring") -self:AddTransition("Monitoring","Deploy","Monitoring") -self:AddTransition("Monitoring","Unload","Monitoring") -self:AddTransition("Monitoring","Unloading","Monitoring") -self:AddTransition("Monitoring","Unloaded","Monitoring") -self:AddTransition("Monitoring","Deployed","Monitoring") -self:AddTransition("Monitoring","Home","Monitoring") -self:SetMonitorTimeInterval(30) -self:SetDeployRadius(500,200) -self.PickupCargo={} -self.CarrierHome={} -function self.SetCarrier.OnAfterRemoved(SetCarrier,From,Event,To,CarrierName,Carrier) -self:F({Carrier=Carrier:GetName()}) -self.PickupCargo[Carrier]=nil -self.CarrierHome[Carrier]=nil -end -return self -end -function AI_CARGO_DISPATCHER:SetMonitorTimeInterval(MonitorTimeInterval) -self.MonitorTimeInterval=MonitorTimeInterval -return self -end -function AI_CARGO_DISPATCHER:SetHomeZone(HomeZone) -self.HomeZone=HomeZone -return self -end -function AI_CARGO_DISPATCHER:SetPickupRadius(OuterRadius,InnerRadius) -OuterRadius=OuterRadius or 0 -InnerRadius=InnerRadius or OuterRadius -self.PickupOuterRadius=OuterRadius -self.PickupInnerRadius=InnerRadius -return self -end -function AI_CARGO_DISPATCHER:SetPickupSpeed(MaxSpeed,MinSpeed) -MaxSpeed=MaxSpeed or 999 -MinSpeed=MinSpeed or MaxSpeed -self.PickupMinSpeed=MinSpeed -self.PickupMaxSpeed=MaxSpeed -return self -end -function AI_CARGO_DISPATCHER:SetDeployRadius(OuterRadius,InnerRadius) -OuterRadius=OuterRadius or 0 -InnerRadius=InnerRadius or OuterRadius -self.DeployOuterRadius=OuterRadius -self.DeployInnerRadius=InnerRadius -return self -end -function AI_CARGO_DISPATCHER:SetDeploySpeed(MaxSpeed,MinSpeed) -MaxSpeed=MaxSpeed or 999 -MinSpeed=MinSpeed or MaxSpeed -self.DeployMinSpeed=MinSpeed -self.DeployMaxSpeed=MaxSpeed -return self -end -function AI_CARGO_DISPATCHER:SetPickupHeight(MaxHeight,MinHeight) -MaxHeight=MaxHeight or 200 -MinHeight=MinHeight or MaxHeight -self.PickupMinHeight=MinHeight -self.PickupMaxHeight=MaxHeight -return self -end -function AI_CARGO_DISPATCHER:SetDeployHeight(MaxHeight,MinHeight) -MaxHeight=MaxHeight or 200 -MinHeight=MinHeight or MaxHeight -self.DeployMinHeight=MinHeight -self.DeployMaxHeight=MaxHeight -return self -end -function AI_CARGO_DISPATCHER:onafterMonitor() -self:F("Carriers") -self.SetCarrier:Flush() -for CarrierGroupName,Carrier in pairs(self.SetCarrier:GetSet())do -local Carrier=Carrier -if Carrier:IsAlive()~=nil then -local AI_Cargo=self.AI_Cargo[Carrier] -if not AI_Cargo then -self.AI_Cargo[Carrier]=self:AICargo(Carrier,self.SetCargo,self.CombatRadius) -AI_Cargo=self.AI_Cargo[Carrier] -function AI_Cargo.OnAfterPickup(AI_Cargo,CarrierGroup,From,Event,To,Coordinate,Speed,Height,PickupZone) -self:Pickup(CarrierGroup,Coordinate,Speed,Height,PickupZone) -end -function AI_Cargo.OnAfterLoad(AI_Cargo,CarrierGroup,From,Event,To,PickupZone) -self:Load(CarrierGroup,PickupZone) -end -function AI_Cargo.OnAfterBoard(AI_Cargo,CarrierGroup,From,Event,To,Cargo,CarrierUnit,PickupZone) -self:Loading(CarrierGroup,Cargo,CarrierUnit,PickupZone) -end -function AI_Cargo.OnAfterLoaded(AI_Cargo,CarrierGroup,From,Event,To,Cargo,CarrierUnit,PickupZone) -self:Loaded(CarrierGroup,Cargo,CarrierUnit,PickupZone) -end -function AI_Cargo.OnAfterPickedUp(AI_Cargo,CarrierGroup,From,Event,To,PickupZone) -self:PickedUp(CarrierGroup,PickupZone) -self:Transport(CarrierGroup) -end -function AI_Cargo.OnAfterDeploy(AI_Cargo,CarrierGroup,From,Event,To,Coordinate,Speed,Height,DeployZone) -self:Deploy(CarrierGroup,Coordinate,Speed,Height,DeployZone) -end -function AI_Cargo.OnAfterUnload(AI_Cargo,Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone) -self:Unloading(Carrier,Cargo,CarrierUnit,DeployZone) -end -function AI_Cargo.OnAfterUnboard(AI_Cargo,CarrierGroup,From,Event,To,Cargo,CarrierUnit,DeployZone) -self:Unloading(CarrierGroup,Cargo,CarrierUnit,DeployZone) -end -function AI_Cargo.OnAfterUnloaded(AI_Cargo,Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone) -self:Unloaded(Carrier,Cargo,CarrierUnit,DeployZone) -end -function AI_Cargo.OnAfterDeployed(AI_Cargo,Carrier,From,Event,To,DeployZone) -self:Deployed(Carrier,DeployZone) -end -function AI_Cargo.OnAfterHome(AI_Cargo,Carrier,From,Event,To,Coordinate,Speed,Height,HomeZone) -self:Home(Carrier,Coordinate,Speed,Height,HomeZone) -end -end -self:T({Carrier=CarrierGroupName,IsRelocating=AI_Cargo:IsRelocating(),IsTransporting=AI_Cargo:IsTransporting()}) -if AI_Cargo:IsRelocating()==false and AI_Cargo:IsTransporting()==false then -local PickupCargo=nil -local PickupZone=nil -self.SetCargo:Flush() -for CargoName,Cargo in UTILS.spairs(self.SetCargo:GetSet(),function(t,a,b)return t[a]:GetWeight()=Cargo:GetWeight()then -self.PickupCargo[Carrier]=CargoCoordinate -PickupCargo=Cargo -break -else -local text=string.format("WARNING: Cargo %s is too heavy to be loaded into transport. Cargo weight %.1f > %.1f load capacity of carrier %s.", -tostring(Cargo:GetName()),Cargo:GetWeight(),LargestLoadCapacity,tostring(Carrier:GetName())) -self:I(text) -end -end -end -end -end -if PickupCargo then -self.CarrierHome[Carrier]=nil -local PickupCoordinate=PickupCargo:GetCoordinate():GetRandomCoordinateInRadius(self.PickupOuterRadius,self.PickupInnerRadius) -AI_Cargo:Pickup(PickupCoordinate,math.random(self.PickupMinSpeed,self.PickupMaxSpeed),math.random(self.PickupMinHeight,self.PickupMaxHeight),PickupZone) -break -else -if self.HomeZone then -if not self.CarrierHome[Carrier]then -self.CarrierHome[Carrier]=true -AI_Cargo:Home(self.HomeZone:GetRandomPointVec2(),math.random(self.PickupMinSpeed,self.PickupMaxSpeed),math.random(self.PickupMinHeight,self.PickupMaxHeight),self.HomeZone) -end -end -end -end -end -end -self:__Monitor(self.MonitorTimeInterval) -end -function AI_CARGO_DISPATCHER:onafterStart(From,Event,To) -self:__Monitor(-1) -end -function AI_CARGO_DISPATCHER:onafterTransport(From,Event,To,Carrier,Cargo) -if self.DeployZoneSet then -if self.AI_Cargo[Carrier]:IsTransporting()==true then -local DeployZone=self.DeployZoneSet:GetRandomZone() -local DeployCoordinate=DeployZone:GetCoordinate():GetRandomCoordinateInRadius(self.DeployOuterRadius,self.DeployInnerRadius) -self.AI_Cargo[Carrier]:__Deploy(0.1,DeployCoordinate,math.random(self.DeployMinSpeed,self.DeployMaxSpeed),math.random(self.DeployMinHeight,self.DeployMaxHeight),DeployZone) -end -end -self:F({Carrier=Carrier:GetName(),PickupCargo=self.PickupCargo}) -self.PickupCargo[Carrier]=nil -end -AI_CARGO_DISPATCHER_APC={ -ClassName="AI_CARGO_DISPATCHER_APC", -} -function AI_CARGO_DISPATCHER_APC:New(APCSet,CargoSet,PickupZoneSet,DeployZoneSet,CombatRadius) -local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(APCSet,CargoSet,PickupZoneSet,DeployZoneSet)) -self:SetDeploySpeed(120,70) -self:SetPickupSpeed(120,70) -self:SetPickupRadius(0,0) -self:SetDeployRadius(0,0) -self:SetPickupHeight() -self:SetDeployHeight() -self:SetCombatRadius(CombatRadius) -return self -end -function AI_CARGO_DISPATCHER_APC:AICargo(APC,CargoSet) -local aicargoapc=AI_CARGO_APC:New(APC,CargoSet,self.CombatRadius) -aicargoapc:SetDeployOffRoad(self.deployOffroad,self.deployFormation) -aicargoapc:SetPickupOffRoad(self.pickupOffroad,self.pickupFormation) -return aicargoapc -end -function AI_CARGO_DISPATCHER_APC:SetCombatRadius(CombatRadius) -self.CombatRadius=CombatRadius or 0 -return self -end -function AI_CARGO_DISPATCHER_APC:SetOffRoad(Offroad,Formation) -self:SetPickupOffRoad(Offroad,Formation) -self:SetDeployOffRoad(Offroad,Formation) -return self -end -function AI_CARGO_DISPATCHER_APC:SetPickupOffRoad(Offroad,Formation) -self.pickupOffroad=Offroad -self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad -return self -end -function AI_CARGO_DISPATCHER_APC:SetDeployOffRoad(Offroad,Formation) -self.deployOffroad=Offroad -self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad -return self -end -AI_CARGO_DISPATCHER_HELICOPTER={ -ClassName="AI_CARGO_DISPATCHER_HELICOPTER", -} -function AI_CARGO_DISPATCHER_HELICOPTER:New(HelicopterSet,CargoSet,PickupZoneSet,DeployZoneSet) -local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(HelicopterSet,CargoSet,PickupZoneSet,DeployZoneSet)) -self:SetPickupSpeed(350,150) -self:SetDeploySpeed(350,150) -self:SetPickupRadius(0,0) -self:SetDeployRadius(0,0) -self:SetPickupHeight(500,200) -self:SetDeployHeight(500,200) -return self -end -function AI_CARGO_DISPATCHER_HELICOPTER:AICargo(Helicopter,CargoSet) -return AI_CARGO_HELICOPTER:New(Helicopter,CargoSet) -end -AI_CARGO_DISPATCHER_AIRPLANE={ -ClassName="AI_CARGO_DISPATCHER_AIRPLANE", -} -function AI_CARGO_DISPATCHER_AIRPLANE:New(AirplaneSet,CargoSet,PickupZoneSet,DeployZoneSet) -local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(AirplaneSet,CargoSet,PickupZoneSet,DeployZoneSet)) -self:SetPickupSpeed(1200,600) -self:SetDeploySpeed(1200,600) -self:SetPickupRadius(0,0) -self:SetDeployRadius(0,0) -self:SetPickupHeight(8000,6000) -self:SetDeployHeight(8000,6000) -self:SetMonitorTimeInterval(600) -return self -end -function AI_CARGO_DISPATCHER_AIRPLANE:AICargo(Airplane,CargoSet) -return AI_CARGO_AIRPLANE:New(Airplane,CargoSet) -end -AI_CARGO_DISPATCHER_SHIP={ -ClassName="AI_CARGO_DISPATCHER_SHIP" -} -function AI_CARGO_DISPATCHER_SHIP:New(ShipSet,CargoSet,PickupZoneSet,DeployZoneSet,ShippingLane) -local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(ShipSet,CargoSet,PickupZoneSet,DeployZoneSet)) -self:SetPickupSpeed(60,10) -self:SetDeploySpeed(60,10) -self:SetPickupRadius(500,6000) -self:SetDeployRadius(500,6000) -self:SetPickupHeight(0,0) -self:SetDeployHeight(0,0) -self:SetShippingLane(ShippingLane) -self:SetMonitorTimeInterval(600) -return self -end -function AI_CARGO_DISPATCHER_SHIP:SetShippingLane(ShippingLane) -self.ShippingLane=ShippingLane -return self -end -function AI_CARGO_DISPATCHER_SHIP:AICargo(Ship,CargoSet) -return AI_CARGO_SHIP:New(Ship,CargoSet,0,self.ShippingLane) -end -do -ACT_ASSIGN={ -ClassName="ACT_ASSIGN", -} -function ACT_ASSIGN:New() -local self=BASE:Inherit(self,FSM_PROCESS:New("ACT_ASSIGN")) -self:AddTransition("UnAssigned","Start","Waiting") -self:AddTransition("Waiting","Assign","Assigned") -self:AddTransition("Waiting","Reject","Rejected") -self:AddTransition("*","Fail","Failed") -self:AddEndState("Assigned") -self:AddEndState("Rejected") -self:AddEndState("Failed") -self:SetStartState("UnAssigned") -return self -end -end -do -ACT_ASSIGN_ACCEPT={ -ClassName="ACT_ASSIGN_ACCEPT", -} -function ACT_ASSIGN_ACCEPT:New(TaskBriefing) -local self=BASE:Inherit(self,ACT_ASSIGN:New()) -self.TaskBriefing=TaskBriefing -return self -end -function ACT_ASSIGN_ACCEPT:Init(FsmAssign) -self.TaskBriefing=FsmAssign.TaskBriefing -end -function ACT_ASSIGN_ACCEPT:onafterStart(ProcessUnit,Task,From,Event,To) -self:__Assign(1) -end -function ACT_ASSIGN_ACCEPT:onenterAssigned(ProcessUnit,Task,From,Event,To,TaskGroup) -self.Task:Assign(ProcessUnit,ProcessUnit:GetPlayerName()) -end -end -do -ACT_ASSIGN_MENU_ACCEPT={ -ClassName="ACT_ASSIGN_MENU_ACCEPT", -} -function ACT_ASSIGN_MENU_ACCEPT:New(TaskBriefing) -local self=BASE:Inherit(self,ACT_ASSIGN:New()) -self.TaskBriefing=TaskBriefing -return self -end -function ACT_ASSIGN_MENU_ACCEPT:Init(TaskBriefing) -self.TaskBriefing=TaskBriefing -return self -end -function ACT_ASSIGN_MENU_ACCEPT:onafterStart(ProcessUnit,Task,From,Event,To) -self:GetCommandCenter():MessageToGroup("Task "..self.Task:GetName().." has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!",ProcessUnit:GetGroup(),120) -local TaskGroup=ProcessUnit:GetGroup() -self.Menu=MENU_GROUP:New(TaskGroup,"Task "..self.Task:GetName().." CONFIRMATION") -self.MenuAcceptTask=MENU_GROUP_COMMAND:New(TaskGroup,"Accept task "..self.Task:GetName(),self.Menu,self.MenuAssign,self,TaskGroup) -self.MenuRejectTask=MENU_GROUP_COMMAND:New(TaskGroup,"Reject task "..self.Task:GetName(),self.Menu,self.MenuReject,self,TaskGroup) -self:__Reject(120,TaskGroup) -end -function ACT_ASSIGN_MENU_ACCEPT:MenuAssign(TaskGroup) -self:__Assign(-1,TaskGroup) -end -function ACT_ASSIGN_MENU_ACCEPT:MenuReject(TaskGroup) -self:__Reject(-1,TaskGroup) -end -function ACT_ASSIGN_MENU_ACCEPT:onafterAssign(ProcessUnit,Task,From,Event,To,TaskGroup) -self.Menu:Remove() -end -function ACT_ASSIGN_MENU_ACCEPT:onafterReject(ProcessUnit,Task,From,Event,To,TaskGroup) -self:F({TaskGroup=TaskGroup}) -self.Menu:Remove() -self.Task:RejectGroup(TaskGroup) -end -function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned(ProcessUnit,Task,From,Event,To,TaskGroup) -self.Task:Assign(ProcessUnit,ProcessUnit:GetPlayerName()) -end -end -do -ACT_ROUTE={ -ClassName="ACT_ROUTE", -} -function ACT_ROUTE:New() -local self=BASE:Inherit(self,FSM_PROCESS:New("ACT_ROUTE")) -self:AddTransition("*","Reset","None") -self:AddTransition("None","Start","Routing") -self:AddTransition("*","Report","*") -self:AddTransition("Routing","Route","Routing") -self:AddTransition("Routing","Pause","Pausing") -self:AddTransition("Routing","Arrive","Arrived") -self:AddTransition("*","Cancel","Cancelled") -self:AddTransition("Arrived","Success","Success") -self:AddTransition("*","Fail","Failed") -self:AddTransition("","","") -self:AddTransition("","","") -self:AddEndState("Arrived") -self:AddEndState("Failed") -self:AddEndState("Cancelled") -self:SetStartState("None") -self:SetRouteMode("C") -return self -end -function ACT_ROUTE:SetMenuCancel(MenuGroup,MenuText,ParentMenu,MenuTime,MenuTag) -self.CancelMenuGroupCommand=MENU_GROUP_COMMAND:New( -MenuGroup, -MenuText, -ParentMenu, -self.MenuCancel, -self -):SetTime(MenuTime):SetTag(MenuTag) -ParentMenu:SetTime(MenuTime) -ParentMenu:Remove(MenuTime,MenuTag) -return self -end -function ACT_ROUTE:SetRouteMode(RouteMode) -self.RouteMode=RouteMode -return self -end -function ACT_ROUTE:GetRouteText(Controllable) -local RouteText="" -local Coordinate=nil -if self.Coordinate then -Coordinate=self.Coordinate -end -if self.Zone then -Coordinate=self.Zone:GetPointVec3(self.Altitude) -Coordinate:SetHeading(self.Heading) -end -local Task=self:GetTask() -local CC=self:GetTask():GetMission():GetCommandCenter() -if CC then -if CC:IsModeWWII()then -local ShortestDistance=0 -local ShortestReferencePoint=nil -local ShortestReferenceName="" -self:F({CC.ReferencePoints}) -for ZoneName,Zone in pairs(CC.ReferencePoints)do -self:F({ZoneName=ZoneName}) -local Zone=Zone -local ZoneCoord=Zone:GetCoordinate() -local ZoneDistance=ZoneCoord:Get2DDistance(Coordinate) -self:F({ShortestDistance,ShortestReferenceName}) -if ShortestDistance==0 or ZoneDistance=self.DisplayInterval then -self:T({HasArrived=HasArrived}) -if not HasArrived then -self:Report() -end -self.DisplayCount=1 -else -self.DisplayCount=self.DisplayCount+1 -end -if HasArrived then -self:__Arrive(1) -else -self:__Route(1) -end -return HasArrived -end -return false -end -end -do -ACT_ROUTE_POINT={ -ClassName="ACT_ROUTE_POINT", -} -function ACT_ROUTE_POINT:New(Coordinate,Range) -local self=BASE:Inherit(self,ACT_ROUTE:New()) -self.Coordinate=Coordinate -self.Range=Range or 0 -self.DisplayInterval=30 -self.DisplayCount=30 -self.DisplayMessage=true -self.DisplayTime=10 -return self -end -function ACT_ROUTE_POINT:Init(FsmRoute) -self.Coordinate=FsmRoute.Coordinate -self.Range=FsmRoute.Range or 0 -self.DisplayInterval=30 -self.DisplayCount=30 -self.DisplayMessage=true -self.DisplayTime=10 -self:SetStartState("None") -end -function ACT_ROUTE_POINT:SetCoordinate(Coordinate) -self:F2({Coordinate}) -self.Coordinate=Coordinate -end -function ACT_ROUTE_POINT:GetCoordinate() -self:F2({self.Coordinate}) -return self.Coordinate -end -function ACT_ROUTE_POINT:SetRange(Range) -self:F2({Range}) -self.Range=Range or 10000 -end -function ACT_ROUTE_POINT:GetRange() -self:F2({self.Range}) -return self.Range -end -function ACT_ROUTE_POINT:onfuncHasArrived(ProcessUnit) -if ProcessUnit:IsAlive()then -local Distance=self.Coordinate:Get2DDistance(ProcessUnit:GetCoordinate()) -if Distance<=self.Range then -local RouteText="Task \""..self:GetTask():GetName().."\", you have arrived." -self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Information) -return true -end -end -return false -end -function ACT_ROUTE_POINT:onafterReport(ProcessUnit,From,Event,To) -local RouteText="Task \""..self:GetTask():GetName().."\", "..self:GetRouteText(ProcessUnit) -self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Update) -end -end -do -ACT_ROUTE_ZONE={ -ClassName="ACT_ROUTE_ZONE", -} -function ACT_ROUTE_ZONE:New(Zone) -local self=BASE:Inherit(self,ACT_ROUTE:New()) -self.Zone=Zone -self.DisplayInterval=30 -self.DisplayCount=30 -self.DisplayMessage=true -self.DisplayTime=10 -return self -end -function ACT_ROUTE_ZONE:Init(FsmRoute) -self.Zone=FsmRoute.Zone -self.DisplayInterval=30 -self.DisplayCount=30 -self.DisplayMessage=true -self.DisplayTime=10 -end -function ACT_ROUTE_ZONE:SetZone(Zone,Altitude,Heading) -self.Zone=Zone -self.Altitude=Altitude -self.Heading=Heading -end -function ACT_ROUTE_ZONE:GetZone() -return self.Zone -end -function ACT_ROUTE_ZONE:onfuncHasArrived(ProcessUnit) -if ProcessUnit:IsInZone(self.Zone)then -local RouteText="Task \""..self:GetTask():GetName().."\", you have arrived within the zone." -self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Information) -end -return ProcessUnit:IsInZone(self.Zone) -end -function ACT_ROUTE_ZONE:onafterReport(ProcessUnit,From,Event,To) -self:F({ProcessUnit=ProcessUnit}) -local RouteText="Task \""..self:GetTask():GetName().."\", "..self:GetRouteText(ProcessUnit) -self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Update) -end -end -do -ACT_ACCOUNT={ -ClassName="ACT_ACCOUNT", -TargetSetUnit=nil, -} -function ACT_ACCOUNT:New() -local self=BASE:Inherit(self,FSM_PROCESS:New()) -self:AddTransition("Assigned","Start","Waiting") -self:AddTransition("*","Wait","Waiting") -self:AddTransition("*","Report","Report") -self:AddTransition("*","Event","Account") -self:AddTransition("Account","Player","AccountForPlayer") -self:AddTransition("Account","Other","AccountForOther") -self:AddTransition({"Account","AccountForPlayer","AccountForOther"},"More","Wait") -self:AddTransition({"Account","AccountForPlayer","AccountForOther"},"NoMore","Accounted") -self:AddTransition("*","Fail","Failed") -self:AddEndState("Failed") -self:SetStartState("Assigned") -return self -end -function ACT_ACCOUNT:onafterStart(ProcessUnit,From,Event,To) -self:HandleEvent(EVENTS.Dead,self.onfuncEventDead) -self:HandleEvent(EVENTS.Crash,self.onfuncEventCrash) -self:HandleEvent(EVENTS.Hit) -self:__Wait(1) -end -function ACT_ACCOUNT:onenterWaiting(ProcessUnit,From,Event,To) -if self.DisplayCount>=self.DisplayInterval then -self:Report() -self.DisplayCount=1 -else -self.DisplayCount=self.DisplayCount+1 -end -return true -end -function ACT_ACCOUNT:onafterEvent(ProcessUnit,From,Event,To,Event) -self:__NoMore(1) -end -end -do -ACT_ACCOUNT_DEADS={ -ClassName="ACT_ACCOUNT_DEADS", -} -function ACT_ACCOUNT_DEADS:New() -local self=BASE:Inherit(self,ACT_ACCOUNT:New()) -self.DisplayInterval=30 -self.DisplayCount=30 -self.DisplayMessage=true -self.DisplayTime=10 -self.DisplayCategory="HQ" -return self -end -function ACT_ACCOUNT_DEADS:Init(FsmAccount) -self.Task=self:GetTask() -self.TaskName=self.Task:GetName() -end -function ACT_ACCOUNT_DEADS:onenterReport(ProcessUnit,Task,From,Event,To) -local MessageText="Your group with assigned "..self.TaskName.." task has "..Task.TargetSetUnit:GetUnitTypesText().." targets left to be destroyed." -self:GetCommandCenter():MessageTypeToGroup(MessageText,ProcessUnit:GetGroup(),MESSAGE.Type.Information) -end -function ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit,Task,From,Event,To,EventData) -self:T({ProcessUnit:GetName(),Task:GetName(),From,Event,To,EventData}) -if Task.TargetSetUnit:FindUnit(EventData.IniUnitName)then -local PlayerName=ProcessUnit:GetPlayerName() -local PlayerHit=self.PlayerHits and self.PlayerHits[EventData.IniUnitName] -if PlayerHit==PlayerName then -self:Player(EventData) -else -self:Other(EventData) -end -end -end -function ACT_ACCOUNT_DEADS:onenterAccountForPlayer(ProcessUnit,Task,From,Event,To,EventData) -self:T({ProcessUnit:GetName(),Task:GetName(),From,Event,To,EventData}) -local TaskGroup=ProcessUnit:GetGroup() -Task.TargetSetUnit:Remove(EventData.IniUnitName) -local MessageText="You have destroyed a target.\nYour group assigned with task "..self.TaskName.." has\n"..Task.TargetSetUnit:Count().." targets ( "..Task.TargetSetUnit:GetUnitTypesText().." ) left to be destroyed." -self:GetCommandCenter():MessageTypeToGroup(MessageText,ProcessUnit:GetGroup(),MESSAGE.Type.Information) -local PlayerName=ProcessUnit:GetPlayerName() -Task:AddProgress(PlayerName,"Destroyed "..EventData.IniTypeName,timer.getTime(),1) -if Task.TargetSetUnit:Count()>0 then -self:__More(1) -else -self:__NoMore(1) -end -end -function ACT_ACCOUNT_DEADS:onenterAccountForOther(ProcessUnit,Task,From,Event,To,EventData) -self:T({ProcessUnit:GetName(),Task:GetName(),From,Event,To,EventData}) -local TaskGroup=ProcessUnit:GetGroup() -Task.TargetSetUnit:Remove(EventData.IniUnitName) -local MessageText="One of the task targets has been destroyed.\nYour group assigned with task "..self.TaskName.." has\n"..Task.TargetSetUnit:Count().." targets ( "..Task.TargetSetUnit:GetUnitTypesText().." ) left to be destroyed." -self:GetCommandCenter():MessageTypeToGroup(MessageText,ProcessUnit:GetGroup(),MESSAGE.Type.Information) -if Task.TargetSetUnit:Count()>0 then -self:__More(1) -else -self:__NoMore(1) -end -end -function ACT_ACCOUNT_DEADS:OnEventHit(EventData) -self:T({"EventDead",EventData}) -if EventData.IniPlayerName and EventData.TgtDCSUnitName then -self.PlayerHits=self.PlayerHits or{} -self.PlayerHits[EventData.TgtDCSUnitName]=EventData.IniPlayerName -end -end -function ACT_ACCOUNT_DEADS:onfuncEventDead(EventData) -self:T({"EventDead",EventData}) -if EventData.IniDCSUnit then -self:Event(EventData) -end -end -function ACT_ACCOUNT_DEADS:onfuncEventCrash(EventData) -self:T({"EventDead",EventData}) -if EventData.IniDCSUnit then -self:Event(EventData) -end -end -end -do -ACT_ASSIST={ -ClassName="ACT_ASSIST", -} -function ACT_ASSIST:New() -local self=BASE:Inherit(self,FSM_PROCESS:New("ACT_ASSIST")) -self:AddTransition("None","Start","AwaitSmoke") -self:AddTransition("AwaitSmoke","Next","Smoking") -self:AddTransition("Smoking","Next","AwaitSmoke") -self:AddTransition("*","Stop","Success") -self:AddTransition("*","Fail","Failed") -self:AddEndState("Failed") -self:AddEndState("Success") -self:SetStartState("None") -return self -end -function ACT_ASSIST:onafterStart(ProcessUnit,From,Event,To) -local ProcessGroup=ProcessUnit:GetGroup() -local MissionMenu=self:GetMission():GetMenu(ProcessGroup) -local function MenuSmoke(MenuParam) -local self=MenuParam.self -local SmokeColor=MenuParam.SmokeColor -self.SmokeColor=SmokeColor -self:__Next(1) -end -self.Menu=MENU_GROUP:New(ProcessGroup,"Target acquisition",MissionMenu) -self.MenuSmokeBlue=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop blue smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Blue}) -self.MenuSmokeGreen=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop green smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Green}) -self.MenuSmokeOrange=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop Orange smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Orange}) -self.MenuSmokeRed=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop Red smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Red}) -self.MenuSmokeWhite=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop White smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.White}) -end -function ACT_ASSIST:onafterStop(ProcessUnit,From,Event,To) -self.Menu:Remove() -end -end -do -ACT_ASSIST_SMOKE_TARGETS_ZONE={ -ClassName="ACT_ASSIST_SMOKE_TARGETS_ZONE", -} -function ACT_ASSIST_SMOKE_TARGETS_ZONE:New(TargetSetUnit,TargetZone) -local self=BASE:Inherit(self,ACT_ASSIST:New()) -self.TargetSetUnit=TargetSetUnit -self.TargetZone=TargetZone -return self -end -function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init(FsmSmoke) -self.TargetSetUnit=FsmSmoke.TargetSetUnit -self.TargetZone=FsmSmoke.TargetZone -end -function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init(TargetSetUnit,TargetZone) -self.TargetSetUnit=TargetSetUnit -self.TargetZone=TargetZone -return self -end -function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking(ProcessUnit,From,Event,To) -self.TargetSetUnit:ForEachUnit( -function(SmokeUnit) -if math.random(1,(100*self.TargetSetUnit:Count())/4)<=100 then -SCHEDULER:New(self, -function() -if SmokeUnit:IsAlive()then -SmokeUnit:Smoke(self.SmokeColor,150) -end -end,{},math.random(10,60) -) -end -end -) -end -end -COMMANDCENTER={ -ClassName="COMMANDCENTER", -CommandCenterName="", -CommandCenterCoalition=nil, -CommandCenterPositionable=nil, -Name="", -ReferencePoints={}, -ReferenceNames={}, -CommunicationMode="80", -} -COMMANDCENTER.AutoAssignMethods={ -["Random"]=1, -["Distance"]=2, -["Priority"]=3, -} -function COMMANDCENTER:New(CommandCenterPositionable,CommandCenterName) -local self=BASE:Inherit(self,BASE:New()) -self.CommandCenterPositionable=CommandCenterPositionable -self.CommandCenterName=CommandCenterName or CommandCenterPositionable:GetName() -self.CommandCenterCoalition=CommandCenterPositionable:GetCoalition() -self.Missions={} -self:SetAutoAssignTasks(false) -self:SetAutoAcceptTasks(true) -self:SetAutoAssignMethod(COMMANDCENTER.AutoAssignMethods.Distance) -self:SetFlashStatus(false) -self:HandleEvent(EVENTS.Birth, -function(self,EventData) -if EventData.IniObjectCategory==1 then -local EventGroup=GROUP:Find(EventData.IniDCSGroup) -if EventGroup and EventGroup:IsAlive()and self:HasGroup(EventGroup)then -local CommandCenterMenu=MENU_GROUP:New(EventGroup,self:GetText()) -local MenuReporting=MENU_GROUP:New(EventGroup,"Missions Reports",CommandCenterMenu) -local MenuMissionsSummary=MENU_GROUP_COMMAND:New(EventGroup,"Missions Status Report",MenuReporting,self.ReportSummary,self,EventGroup) -local MenuMissionsDetails=MENU_GROUP_COMMAND:New(EventGroup,"Missions Players Report",MenuReporting,self.ReportMissionsPlayers,self,EventGroup) -self:ReportSummary(EventGroup) -local PlayerUnit=EventData.IniUnit -for MissionID,Mission in pairs(self:GetMissions())do -local Mission=Mission -local PlayerGroup=EventData.IniGroup -Mission:JoinUnit(PlayerUnit,PlayerGroup) -end -self:SetMenu() -end -end -end -) -self:HandleEvent(EVENTS.MissionEnd, -function(self,EventData) -local PlayerUnit=EventData.IniUnit -for MissionID,Mission in pairs(self:GetMissions())do -local Mission=Mission -Mission:Stop() -end -end -) -self:HandleEvent(EVENTS.PlayerLeaveUnit, -function(self,EventData) -local PlayerUnit=EventData.IniUnit -for MissionID,Mission in pairs(self:GetMissions())do -local Mission=Mission -if Mission:IsENGAGED()then -Mission:AbortUnit(PlayerUnit) -end -end -end -) -self:HandleEvent(EVENTS.Crash, -function(self,EventData) -local PlayerUnit=EventData.IniUnit -for MissionID,Mission in pairs(self:GetMissions())do -local Mission=Mission -if Mission:IsENGAGED()then -Mission:CrashUnit(PlayerUnit) -end -end -end -) -self:SetMenu() -_SETTINGS:SetSystemMenu(CommandCenterPositionable) -self:SetCommandMenu() -return self -end -function COMMANDCENTER:GetName() -return self.CommandCenterName -end -function COMMANDCENTER:GetText() -return"Command Center ["..self.CommandCenterName.."]" -end -function COMMANDCENTER:GetShortText() -return"CC ["..self.CommandCenterName.."]" -end -function COMMANDCENTER:GetCoalition() -return self.CommandCenterCoalition -end -function COMMANDCENTER:GetPositionable() -return self.CommandCenterPositionable -end -function COMMANDCENTER:GetMissions() -return self.Missions or{} -end -function COMMANDCENTER:AddMission(Mission) -self.Missions[Mission]=Mission -return Mission -end -function COMMANDCENTER:RemoveMission(Mission) -self.Missions[Mission]=nil -return Mission -end -function COMMANDCENTER:SetReferenceZones(ReferenceZonePrefix) -local MatchPattern="(.*)#(.*)" -self:F({MatchPattern=MatchPattern}) -for ReferenceZoneName in pairs(_DATABASE.ZONENAMES)do -local ZoneName,ReferenceName=string.match(ReferenceZoneName,MatchPattern) -self:F({ZoneName=ZoneName,ReferenceName=ReferenceName}) -if ZoneName and ReferenceName and ZoneName==ReferenceZonePrefix then -self.ReferencePoints[ReferenceZoneName]=ZONE:New(ReferenceZoneName) -self.ReferenceNames[ReferenceZoneName]=ReferenceName -end -end -return self -end -function COMMANDCENTER:SetModeWWII() -self.CommunicationMode="WWII" -return self -end -function COMMANDCENTER:IsModeWWII() -return self.CommunicationMode=="WWII" -end -function COMMANDCENTER:SetMenu() -self:F2() -local MenuTime=timer.getTime() -for MissionID,Mission in pairs(self:GetMissions()or{})do -local Mission=Mission -Mission:SetMenu(MenuTime) -end -for MissionID,Mission in pairs(self:GetMissions()or{})do -Mission=Mission -Mission:RemoveMenu(MenuTime) -end -end -function COMMANDCENTER:GetMenu(TaskGroup) -local MenuTime=timer.getTime() -self.CommandCenterMenus=self.CommandCenterMenus or{} -local CommandCenterMenu -local CommandCenterText=self:GetText() -CommandCenterMenu=MENU_GROUP:New(TaskGroup,CommandCenterText):SetTime(MenuTime) -self.CommandCenterMenus[TaskGroup]=CommandCenterMenu -if self.AutoAssignTasks==false then -local AssignTaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,"Assign Task",CommandCenterMenu,self.AssignTask,self,TaskGroup):SetTime(MenuTime):SetTag("AutoTask") -end -CommandCenterMenu:Remove(MenuTime,"AutoTask") -return self.CommandCenterMenus[TaskGroup] -end -function COMMANDCENTER:AssignTask(TaskGroup) -local Tasks={} -local AssignPriority=99999999 -local AutoAssignMethod=self.AutoAssignMethod -for MissionID,Mission in pairs(self:GetMissions())do -local Mission=Mission -local MissionTasks=Mission:GetGroupTasks(TaskGroup) -for MissionTaskName,MissionTask in pairs(MissionTasks or{})do -local MissionTask=MissionTask -if MissionTask:IsStatePlanned()or MissionTask:IsStateReplanned()or MissionTask:IsStateAssigned()then -local TaskPriority=MissionTask:GetAutoAssignPriority(self.AutoAssignMethod,self,TaskGroup) -if TaskPriority Adding TASK ",MissionName=self:GetName(),TaskName=TaskName}) -self.Tasks[TaskName]=Task -self:GetCommandCenter():SetMenu() -return Task -end -function MISSION:RemoveTask(Task) -local TaskName=Task:GetTaskName() -self:I({"<== Removing TASK ",MissionName=self:GetName(),TaskName=TaskName}) -self:F(TaskName) -self.Tasks[TaskName]=self.Tasks[TaskName]or{n=0} -self.Tasks[TaskName]=nil -Task=nil -collectgarbage() -self:GetCommandCenter():SetMenu() -return nil -end -function MISSION:IsCOMPLETED() -return self:Is("COMPLETED") -end -function MISSION:IsIDLE() -return self:Is("IDLE") -end -function MISSION:IsENGAGED() -return self:Is("ENGAGED") -end -function MISSION:IsFAILED() -return self:Is("FAILED") -end -function MISSION:IsHOLD() -return self:Is("HOLD") -end -function MISSION:HasGroup(TaskGroup) -local Has=false -for TaskID,Task in pairs(self:GetTasks())do -local Task=Task -if Task:HasGroup(TaskGroup)then -Has=true -break -end -end -return Has -end -function MISSION:GetTasksRemaining() -local TasksRemaining=0 -for TaskID,Task in pairs(self:GetTasks())do -local Task=Task -if Task:IsStateSuccess()or Task:IsStateFailed()then -else -TasksRemaining=TasksRemaining+1 -end -end -return TasksRemaining -end -function MISSION:GetTaskTypes() -local TaskTypeList={} -local TasksRemaining=0 -for TaskID,Task in pairs(self:GetTasks())do -local Task=Task -local TaskType=Task:GetType() -TaskTypeList[TaskType]=TaskType -end -return TaskTypeList -end -function MISSION:AddPlayerName(PlayerName) -self.PlayerNames=self.PlayerNames or{} -self.PlayerNames[PlayerName]=PlayerName -return self -end -function MISSION:GetPlayerNames() -return self.PlayerNames -end -function MISSION:ReportBriefing() -local Report=REPORT:New() -local Name=self:GetText() -local Status="<"..self:GetState()..">" -Report:Add(string.format('%s - %s - Mission Briefing Report',Name,Status)) -Report:Add(self.MissionBriefing) -return Report:Text() -end -function MISSION:ReportPlayersPerTask(ReportGroup) -local Report=REPORT:New() -local Name=self:GetText() -local Status="<"..self:GetState()..">" -Report:Add(string.format('%s - %s - Players per Task Report',Name,Status)) -local PlayerList={} -for TaskID,Task in pairs(self:GetTasks())do -local Task=Task -local PlayerNames=Task:GetPlayerNames() -for PlayerName,PlayerGroup in pairs(PlayerNames)do -PlayerList[PlayerName]=Task:GetName() -end -end -for PlayerName,TaskName in pairs(PlayerList)do -Report:Add(string.format(' - Player (%s): Task "%s"',PlayerName,TaskName)) -end -return Report:Text() -end -function MISSION:ReportPlayersProgress(ReportGroup) -local Report=REPORT:New() -local Name=self:GetText() -local Status="<"..self:GetState()..">" -Report:Add(string.format('%s - %s - Players per Task Progress Report',Name,Status)) -local PlayerList={} -for TaskID,Task in pairs(self:GetTasks())do -local Task=Task -local TaskName=Task:GetName() -local Goal=Task:GetGoal() -PlayerList[TaskName]=PlayerList[TaskName]or{} -if Goal then -local TotalContributions=Goal:GetTotalContributions() -local PlayerContributions=Goal:GetPlayerContributions() -self:F({TotalContributions=TotalContributions,PlayerContributions=PlayerContributions}) -for PlayerName,PlayerContribution in pairs(PlayerContributions)do -PlayerList[TaskName][PlayerName]=string.format('Player (%s): Task "%s": %d%%',PlayerName,TaskName,PlayerContributions[PlayerName]*100/TotalContributions) -end -else -PlayerList[TaskName]["_"]=string.format('Player (---): Task "%s": %d%%',TaskName,0) -end -end -for TaskName,TaskData in pairs(PlayerList)do -for PlayerName,TaskText in pairs(TaskData)do -Report:Add(string.format(' - %s',TaskText)) -end -end -return Report:Text() -end -function MISSION:MarkTargetLocations(ReportGroup) -local Report=REPORT:New() -local Name=self:GetText() -local Status="<"..self:GetState()..">" -Report:Add(string.format('%s - %s - All Tasks are marked on the map. Select a Task from the Mission Menu and Join the Task!!!',Name,Status)) -for TaskID,Task in UTILS.spairs(self:GetTasks(),function(t,a,b)return t[a]:ReportOrder(ReportGroup)" -Report:Add(string.format('%s - %s - Task Overview Report',Name,Status)) -for TaskID,Task in UTILS.spairs(self:GetTasks(),function(t,a,b)return t[a]:ReportOrder(ReportGroup)" -Report:Add(string.format('%s - %s - %s Tasks Report',Name,Status,TaskStatus)) -local Tasks=0 -for TaskID,Task in UTILS.spairs(self:GetTasks(),function(t,a,b)return t[a]:ReportOrder(ReportGroup)=8 then -break -end -end -return Report:Text() -end -function MISSION:ReportDetails(ReportGroup) -local Report=REPORT:New() -local Name=self:GetText() -local Status="<"..self:GetState()..">" -Report:Add(string.format('%s - %s - Task Detailed Report',Name,Status)) -local TasksRemaining=0 -for TaskID,Task in pairs(self:GetTasks())do -local Task=Task -Report:Add(string.rep("-",140)) -Report:Add(Task:ReportDetails(ReportGroup)) -end -return Report:Text() -end -function MISSION:GetTasks() -return self.Tasks or{} -end -function MISSION:GetGroupTasks(TaskGroup) -local Tasks={} -for TaskID,Task in pairs(self:GetTasks())do -local Task=Task -if Task:HasGroup(TaskGroup)then -Tasks[#Tasks+1]=Task -end -end -return Tasks -end -function MISSION:MenuReportBriefing(ReportGroup) -local Report=self:ReportBriefing() -self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Briefing) -end -function MISSION:MenuMarkTargetLocations(ReportGroup) -local Report=self:MarkTargetLocations(ReportGroup) -self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview) -end -function MISSION:MenuReportTasksSummary(ReportGroup) -local Report=self:ReportSummary(ReportGroup) -self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview) -end -function MISSION:MenuReportTasksPerStatus(ReportGroup,TaskStatus) -local Report=self:ReportOverview(ReportGroup,TaskStatus) -self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview) -end -function MISSION:MenuReportPlayersPerTask(ReportGroup) -local Report=self:ReportPlayersPerTask() -self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview) -end -function MISSION:MenuReportPlayersProgress(ReportGroup) -local Report=self:ReportPlayersProgress() -self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview) -end -TASK={ -ClassName="TASK", -TaskScheduler=nil, -ProcessClasses={}, -Processes={}, -Players=nil, -Scores={}, -Menu={}, -SetGroup=nil, -FsmTemplate=nil, -Mission=nil, -CommandCenter=nil, -TimeOut=0, -AssignedGroups={}, -} -function TASK:New(Mission,SetGroupAssign,TaskName,TaskType,TaskBriefing) -local self=BASE:Inherit(self,FSM_TASK:New(TaskName)) -self:SetStartState("Planned") -self:AddTransition("Planned","Assign","Assigned") -self:AddTransition("Assigned","AssignUnit","Assigned") -self:AddTransition("Assigned","Success","Success") -self:AddTransition("Assigned","Hold","Hold") -self:AddTransition("Assigned","Fail","Failed") -self:AddTransition({"Planned","Assigned"},"Abort","Aborted") -self:AddTransition("Assigned","Cancel","Cancelled") -self:AddTransition("Assigned","Goal","*") -self.Fsm={} -local Fsm=self:GetUnitProcess() -Fsm:SetStartState("Planned") -Fsm:AddProcess("Planned","Accept",ACT_ASSIGN_ACCEPT:New(self.TaskBriefing),{Assigned="Assigned",Rejected="Reject"}) -Fsm:AddTransition("Assigned","Assigned","*") -self:AddTransition("*","PlayerCrashed","*") -self:AddTransition("*","PlayerAborted","*") -self:AddTransition("*","PlayerRejected","*") -self:AddTransition("*","PlayerDead","*") -self:AddTransition({"Failed","Aborted","Cancelled"},"Replan","Planned") -self:AddTransition("*","TimeOut","Cancelled") -self:F("New TASK "..TaskName) -self.Processes={} -self.Mission=Mission -self.CommandCenter=Mission:GetCommandCenter() -self.SetGroup=SetGroupAssign -self:SetType(TaskType) -self:SetName(TaskName) -self:SetID(Mission:GetNextTaskID(self)) -self:SetBriefing(TaskBriefing) -self.TaskInfo=TASKINFO:New(self) -self.TaskProgress={} -return self -end -function TASK:GetUnitProcess(TaskUnit) -if TaskUnit then -return self:GetStateMachine(TaskUnit) -else -self.FsmTemplate=self.FsmTemplate or FSM_PROCESS:New() -return self.FsmTemplate -end -end -function TASK:SetUnitProcess(FsmTemplate) -self.FsmTemplate=FsmTemplate -end -function TASK:JoinUnit(PlayerUnit,PlayerGroup) -self:F({PlayerUnit=PlayerUnit,PlayerGroup=PlayerGroup}) -local PlayerUnitAdded=false -local PlayerGroups=self:GetGroups() -if PlayerGroups:IsIncludeObject(PlayerGroup)then -if self:IsStatePlanned()or self:IsStateReplanned()then -end -if self:IsStateAssigned()then -local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup) -self:F({IsGroupAssigned=IsGroupAssigned}) -if IsGroupAssigned then -self:AssignToUnit(PlayerUnit) -self:MessageToGroups(PlayerUnit:GetPlayerName().." joined Task "..self:GetName()) -end -end -end -return PlayerUnitAdded -end -function TASK:RejectGroup(PlayerGroup) -local PlayerGroups=self:GetGroups() -if PlayerGroups:IsIncludeObject(PlayerGroup)then -if self:IsStatePlanned()then -local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup) -if IsGroupAssigned then -local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName() -self:GetMission():GetCommandCenter():MessageToGroup("Task "..self:GetName().." has been rejected! We will select another task.",PlayerGroup) -self:UnAssignFromGroup(PlayerGroup) -self:PlayerRejected(PlayerGroup:GetUnit(1)) -end -end -end -return self -end -function TASK:AbortGroup(PlayerGroup) -local PlayerGroups=self:GetGroups() -if PlayerGroups:IsIncludeObject(PlayerGroup)then -if self:IsStateAssigned()then -local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup) -if IsGroupAssigned then -local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName() -self:UnAssignFromGroup(PlayerGroup) -PlayerGroups:Flush(self) -local IsRemaining=false -for GroupName,AssignedGroup in pairs(PlayerGroups:GetSet()or{})do -if self:IsGroupAssigned(AssignedGroup)==true then -IsRemaining=true -self:F({Task=self:GetName(),IsRemaining=IsRemaining}) -break -end -end -self:F({Task=self:GetName(),IsRemaining=IsRemaining}) -if IsRemaining==false then -self:Abort() -end -self:PlayerAborted(PlayerGroup:GetUnit(1)) -end -end -end -return self -end -function TASK:CrashGroup(PlayerGroup) -self:F({PlayerGroup=PlayerGroup}) -local PlayerGroups=self:GetGroups() -if PlayerGroups:IsIncludeObject(PlayerGroup)then -if self:IsStateAssigned()then -local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup) -self:F({IsGroupAssigned=IsGroupAssigned}) -if IsGroupAssigned then -local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName() -self:MessageToGroups(PlayerName.." crashed! ") -self:UnAssignFromGroup(PlayerGroup) -PlayerGroups:Flush(self) -local IsRemaining=false -for GroupName,AssignedGroup in pairs(PlayerGroups:GetSet()or{})do -if self:IsGroupAssigned(AssignedGroup)==true then -IsRemaining=true -self:F({Task=self:GetName(),IsRemaining=IsRemaining}) -break -end -end -self:F({Task=self:GetName(),IsRemaining=IsRemaining}) -if IsRemaining==false then -self:Abort() -end -self:PlayerCrashed(PlayerGroup:GetUnit(1)) -end -end -end -return self -end -function TASK:GetMission() -return self.Mission -end -function TASK:GetGroups() -return self.SetGroup -end -function TASK:AddGroups(GroupSet) -GroupSet=GroupSet or SET_GROUP:New() -self.SetGroup:ForEachGroup( -function(GroupItem) -GroupSet:Add(GroupItem:GetName(),GroupItem) -end -) -return GroupSet -end -do -function TASK:IsGroupAssigned(TaskGroup) -local TaskGroupName=TaskGroup:GetName() -if self.AssignedGroups[TaskGroupName]then -return true -end -return false -end -function TASK:SetGroupAssigned(TaskGroup) -local TaskName=self:GetName() -local TaskGroupName=TaskGroup:GetName() -self.AssignedGroups[TaskGroupName]=TaskGroup -self:F(string.format("Task %s is assigned to %s",TaskName,TaskGroupName)) -self:GetMission():SetGroupAssigned(TaskGroup) -local SetAssignedGroups=self:GetGroups() -return self -end -function TASK:ClearGroupAssignment(TaskGroup) -local TaskName=self:GetName() -local TaskGroupName=TaskGroup:GetName() -self.AssignedGroups[TaskGroupName]=nil -self:GetMission():ClearGroupAssignment(TaskGroup) -local SetAssignedGroups=self:GetGroups() -SetAssignedGroups:ForEachGroup( -function(AssignedGroup) -if self:IsGroupAssigned(AssignedGroup)then -else -end -end -) -return self -end -end -do -function TASK:SetAssignMethod(AcceptClass) -local ProcessTemplate=self:GetUnitProcess() -ProcessTemplate:SetProcess("Planned","Accept",AcceptClass) -end -function TASK:AssignToGroup(TaskGroup) -self:F(TaskGroup:GetName()) -local TaskGroupName=TaskGroup:GetName() -local Mission=self:GetMission() -local CommandCenter=Mission:GetCommandCenter() -self:SetGroupAssigned(TaskGroup) -local TaskUnits=TaskGroup:GetUnits() -for UnitID,UnitData in pairs(TaskUnits)do -local TaskUnit=UnitData -local PlayerName=TaskUnit:GetPlayerName() -self:F(PlayerName) -if PlayerName~=nil and PlayerName~=""then -self:AssignToUnit(TaskUnit) -CommandCenter:MessageToGroup( -string.format('Task "%s": Briefing for player (%s):\n%s', -self:GetName(), -PlayerName, -self:GetBriefing() -),TaskGroup -) -end -end -CommandCenter:SetMenu() -self:MenuFlashTaskStatus(TaskGroup,self:GetMission():GetCommandCenter().FlashStatus) -return self -end -function TASK:UnAssignFromGroup(TaskGroup) -self:F2({TaskGroup=TaskGroup:GetName()}) -self:ClearGroupAssignment(TaskGroup) -local TaskUnits=TaskGroup:GetUnits() -for UnitID,UnitData in pairs(TaskUnits)do -local TaskUnit=UnitData -local PlayerName=TaskUnit:GetPlayerName() -if PlayerName~=nil and PlayerName~=""then -self:UnAssignFromUnit(TaskUnit) -end -end -local Mission=self:GetMission() -local CommandCenter=Mission:GetCommandCenter() -CommandCenter:SetMenu() -self:MenuFlashTaskStatus(TaskGroup,false) -end -end -function TASK:HasGroup(FindGroup) -local SetAttackGroup=self:GetGroups() -return SetAttackGroup:FindGroup(FindGroup:GetName()) -end -function TASK:AssignToUnit(TaskUnit) -self:F(TaskUnit:GetName()) -local FsmTemplate=self:GetUnitProcess() -local FsmUnit=self:SetStateMachine(TaskUnit,FsmTemplate:Copy(TaskUnit,self)) -FsmUnit:SetStartState("Planned") -FsmUnit:Accept() -return self -end -function TASK:UnAssignFromUnit(TaskUnit) -self:F(TaskUnit:GetName()) -self:RemoveStateMachine(TaskUnit) -self:RemoveTaskControlMenu(TaskUnit) -return self -end -function TASK:SetTimeOut(Timer) -self:F(Timer) -self.TimeOut=Timer -self:__TimeOut(self.TimeOut) -return self -end -function TASK:MessageToGroups(Message) -self:F({Message=Message}) -local Mission=self:GetMission() -local CC=Mission:GetCommandCenter() -for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do -TaskGroup=TaskGroup -if TaskGroup:IsAlive()==true then -CC:MessageToGroup(Message,TaskGroup,TaskGroup:GetName()) -end -end -end -function TASK:SendBriefingToAssignedGroups() -self:F2() -for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do -if TaskGroup:IsAlive()then -if self:IsGroupAssigned(TaskGroup)then -TaskGroup:Message(self.TaskBriefing,60) -end -end -end -end -function TASK:UnAssignFromGroups() -self:F2() -for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do -if TaskGroup:IsAlive()==true then -if self:IsGroupAssigned(TaskGroup)then -self:UnAssignFromGroup(TaskGroup) -end -end -end -end -function TASK:HasAliveUnits() -self:F() -for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do -if TaskGroup:IsAlive()==true then -if self:IsStateAssigned()then -if self:IsGroupAssigned(TaskGroup)then -for TaskUnitID,TaskUnit in pairs(TaskGroup:GetUnits())do -if TaskUnit:IsAlive()then -self:T({HasAliveUnits=true}) -return true -end -end -end -end -end -end -self:T({HasAliveUnits=false}) -return false -end -function TASK:SetMenu(MenuTime) -self:F({self:GetName(),MenuTime}) -for TaskGroupID,TaskGroupData in pairs(self.SetGroup:GetSet())do -local TaskGroup=TaskGroupData -if TaskGroup:IsAlive()==true and TaskGroup:GetPlayerNames()then -local Mission=self:GetMission() -local MissionMenu=Mission:GetMenu(TaskGroup) -if MissionMenu then -self:SetMenuForGroup(TaskGroup,MenuTime) -end -end -end -end -function TASK:SetMenuForGroup(TaskGroup,MenuTime) -if self:IsStatePlanned()or self:IsStateAssigned()then -self:SetPlannedMenuForGroup(TaskGroup,MenuTime) -if self:IsGroupAssigned(TaskGroup)then -self:SetAssignedMenuForGroup(TaskGroup,MenuTime) -end -end -end -function TASK:SetPlannedMenuForGroup(TaskGroup,MenuTime) -self:F(TaskGroup:GetName()) -local Mission=self:GetMission() -local MissionName=Mission:GetName() -local MissionMenu=Mission:GetMenu(TaskGroup) -local TaskType=self:GetType() -local TaskPlayerCount=self:GetPlayerCount() -local TaskPlayerString=string.format(" (%dp)",TaskPlayerCount) -local TaskText=string.format("%s",self:GetName()) -local TaskName=string.format("%s",self:GetName()) -self.MenuPlanned=self.MenuPlanned or{} -self.MenuPlanned[TaskGroup]=MENU_GROUP_DELAYED:New(TaskGroup,"Join Planned Task",MissionMenu,Mission.MenuReportTasksPerStatus,Mission,TaskGroup,"Planned"):SetTime(MenuTime):SetTag("Tasking") -local TaskTypeMenu=MENU_GROUP_DELAYED:New(TaskGroup,TaskType,self.MenuPlanned[TaskGroup]):SetTime(MenuTime):SetTag("Tasking") -local TaskTypeMenu=MENU_GROUP_DELAYED:New(TaskGroup,TaskText,TaskTypeMenu):SetTime(MenuTime):SetTag("Tasking") -if not Mission:IsGroupAssigned(TaskGroup)then -local JoinTaskMenu=MENU_GROUP_COMMAND_DELAYED:New(TaskGroup,string.format("Join Task"),TaskTypeMenu,self.MenuAssignToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking") -local MarkTaskMenu=MENU_GROUP_COMMAND_DELAYED:New(TaskGroup,string.format("Mark Task Location on Map"),TaskTypeMenu,self.MenuMarkToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking") -end -local ReportTaskMenu=MENU_GROUP_COMMAND_DELAYED:New(TaskGroup,string.format("Report Task Details"),TaskTypeMenu,self.MenuTaskStatus,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking") -return self -end -function TASK:SetAssignedMenuForGroup(TaskGroup,MenuTime) -self:F({TaskGroup:GetName(),MenuTime}) -local TaskType=self:GetType() -local TaskPlayerCount=self:GetPlayerCount() -local TaskPlayerString=string.format(" (%dp)",TaskPlayerCount) -local TaskText=string.format("%s%s",self:GetName(),TaskPlayerString) -local TaskName=string.format("%s",self:GetName()) -for UnitName,TaskUnit in pairs(TaskGroup:GetPlayerUnits())do -local TaskUnit=TaskUnit -if TaskUnit then -local MenuControl=self:GetTaskControlMenu(TaskUnit) -local TaskControl=MENU_GROUP:New(TaskGroup,"Control Task",MenuControl):SetTime(MenuTime):SetTag("Tasking") -if self:IsStateAssigned()then -local TaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Abort Task"),TaskControl,self.MenuTaskAbort,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking") -end -local MarkMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Mark Task Location on Map"),TaskControl,self.MenuMarkToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking") -local TaskTypeMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Report Task Details"),TaskControl,self.MenuTaskStatus,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking") -if not self.FlashTaskStatus then -local TaskFlashStatusMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Flash Task Details"),TaskControl,self.MenuFlashTaskStatus,self,TaskGroup,true):SetTime(MenuTime):SetTag("Tasking") -else -local TaskFlashStatusMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Stop Flash Task Details"),TaskControl,self.MenuFlashTaskStatus,self,TaskGroup,nil):SetTime(MenuTime):SetTag("Tasking") -end -end -end -return self -end -function TASK:RemoveMenu(MenuTime) -self:F({self:GetName(),MenuTime}) -for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do -if TaskGroup:IsAlive()==true then -local TaskGroup=TaskGroup -if TaskGroup:IsAlive()==true and TaskGroup:GetPlayerNames()then -self:RefreshMenus(TaskGroup,MenuTime) -end -end -end -end -function TASK:RefreshMenus(TaskGroup,MenuTime) -self:F({TaskGroup:GetName(),MenuTime}) -local Mission=self:GetMission() -local MissionName=Mission:GetName() -local MissionMenu=Mission:GetMenu(TaskGroup) -local TaskName=self:GetName() -self.MenuPlanned=self.MenuPlanned or{} -local PlannedMenu=self.MenuPlanned[TaskGroup] -self.MenuAssigned=self.MenuAssigned or{} -local AssignedMenu=self.MenuAssigned[TaskGroup] -if PlannedMenu then -self.MenuPlanned[TaskGroup]=PlannedMenu:Remove(MenuTime,"Tasking") -PlannedMenu:Set() -end -if AssignedMenu then -self.MenuAssigned[TaskGroup]=AssignedMenu:Remove(MenuTime,"Tasking") -AssignedMenu:Set() -end -end -function TASK:RemoveAssignedMenuForGroup(TaskGroup) -self:F() -local Mission=self:GetMission() -local MissionName=Mission:GetName() -local MissionMenu=Mission:GetMenu(TaskGroup) -if MissionMenu then -MissionMenu:RemoveSubMenus() -end -end -function TASK:MenuAssignToGroup(TaskGroup) -self:F("Join Task menu selected") -self:AssignToGroup(TaskGroup) -end -function TASK:MenuMarkToGroup(TaskGroup) -self:F() -self:UpdateTaskInfo(self.DetectedItem) -local TargetCoordinates=self.TaskInfo:GetData("Coordinates") -if TargetCoordinates then -for TargetCoordinateID,TargetCoordinate in pairs(TargetCoordinates)do -local Report=REPORT:New():SetIndent(0) -self.TaskInfo:Report(Report,"M",TaskGroup,self) -local MarkText=Report:Text(", ") -self:F({Coordinate=TargetCoordinate,MarkText=MarkText}) -TargetCoordinate:MarkToGroup(MarkText,TaskGroup) -end -else -local TargetCoordinate=self.TaskInfo:GetData("Coordinate") -if TargetCoordinate then -local Report=REPORT:New():SetIndent(0) -self.TaskInfo:Report(Report,"M",TaskGroup,self) -local MarkText=Report:Text(", ") -self:F({Coordinate=TargetCoordinate,MarkText=MarkText}) -TargetCoordinate:MarkToGroup(MarkText,TaskGroup) -end -end -end -function TASK:MenuTaskStatus(TaskGroup) -if TaskGroup:IsAlive()then -local ReportText=self:ReportDetails(TaskGroup) -self:T(ReportText) -self:GetMission():GetCommandCenter():MessageTypeToGroup(ReportText,TaskGroup,MESSAGE.Type.Detailed) -end -end -function TASK:MenuFlashTaskStatus(TaskGroup,Flash) -self.FlashTaskStatus=Flash -if self.FlashTaskStatus then -self.FlashTaskScheduler,self.FlashTaskScheduleID=SCHEDULER:New(self,self.MenuTaskStatus,{TaskGroup},0,60) -else -if self.FlashTaskScheduler then -self.FlashTaskScheduler:Stop(self.FlashTaskScheduleID) -self.FlashTaskScheduler=nil -self.FlashTaskScheduleID=nil -end -end -end -function TASK:MenuTaskAbort(TaskGroup) -self:AbortGroup(TaskGroup) -end -function TASK:GetTaskName() -return self.TaskName -end -function TASK:GetTaskBriefing() -return self.TaskBriefing -end -function TASK:GetProcessTemplate(ProcessName) -local ProcessTemplate=self.ProcessClasses[ProcessName] -return ProcessTemplate -end -function TASK:FailProcesses(TaskUnitName) -for ProcessID,ProcessData in pairs(self.Processes[TaskUnitName])do -local Process=ProcessData -Process.Fsm:Fail() -end -end -function TASK:SetStateMachine(TaskUnit,Fsm) -self:F2({TaskUnit,self.Fsm[TaskUnit]~=nil,Fsm:GetClassNameAndID()}) -self.Fsm[TaskUnit]=Fsm -return Fsm -end -function TASK:GetStateMachine(TaskUnit) -self:F2({TaskUnit,self.Fsm[TaskUnit]~=nil}) -return self.Fsm[TaskUnit] -end -function TASK:RemoveStateMachine(TaskUnit) -self:F({TaskUnit=TaskUnit:GetName(),HasFsm=(self.Fsm[TaskUnit]~=nil)}) -if self.Fsm[TaskUnit]then -self.Fsm[TaskUnit]:Remove() -self.Fsm[TaskUnit]=nil -end -collectgarbage() -self:F("Garbage Collected, Processes should be finalized now ...") -end -function TASK:HasStateMachine(TaskUnit) -self:F({TaskUnit,self.Fsm[TaskUnit]~=nil}) -return(self.Fsm[TaskUnit]~=nil) -end -function TASK:GetScoring() -return self.Mission:GetScoring() -end -function TASK:GetTaskIndex() -local TaskType=self:GetType() -local TaskName=self:GetName() -return TaskType.."."..TaskName -end -function TASK:SetName(TaskName) -self.TaskName=TaskName -end -function TASK:GetName() -return self.TaskName -end -function TASK:SetType(TaskType) -self.TaskType=TaskType -end -function TASK:GetType() -return self.TaskType -end -function TASK:SetID(TaskID) -self.TaskID=TaskID -end -function TASK:GetID() -return self.TaskID -end -function TASK:StateSuccess() -self:SetState(self,"State","Success") -return self -end -function TASK:IsStateSuccess() -return self:Is("Success") -end -function TASK:StateFailed() -self:SetState(self,"State","Failed") -return self -end -function TASK:IsStateFailed() -return self:Is("Failed") -end -function TASK:StatePlanned() -self:SetState(self,"State","Planned") -return self -end -function TASK:IsStatePlanned() -return self:Is("Planned") -end -function TASK:StateAborted() -self:SetState(self,"State","Aborted") -return self -end -function TASK:IsStateAborted() -return self:Is("Aborted") -end -function TASK:StateCancelled() -self:SetState(self,"State","Cancelled") -return self -end -function TASK:IsStateCancelled() -return self:Is("Cancelled") -end -function TASK:StateAssigned() -self:SetState(self,"State","Assigned") -return self -end -function TASK:IsStateAssigned() -return self:Is("Assigned") -end -function TASK:StateHold() -self:SetState(self,"State","Hold") -return self -end -function TASK:IsStateHold() -return self:Is("Hold") -end -function TASK:StateReplanned() -self:SetState(self,"State","Replanned") -return self -end -function TASK:IsStateReplanned() -return self:Is("Replanned") -end -function TASK:GetStateString() -return self:GetState(self,"State") -end -function TASK:SetBriefing(TaskBriefing) -self:F(TaskBriefing) -self.TaskBriefing=TaskBriefing -return self -end -function TASK:GetBriefing() -return self.TaskBriefing -end -function TASK:onenterAssigned(From,Event,To,PlayerUnit,PlayerName) -if From~="Assigned"then -local PlayerNames=self:GetPlayerNames() -local PlayerText=REPORT:New() -for PlayerName,TaskName in pairs(PlayerNames)do -PlayerText:Add(PlayerName) -end -self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." is assigned to players "..PlayerText:Text(",")..". Good Luck!") -self:SetGoalTotal() -if self.Dispatcher then -self:F("Firing Assign event ") -self.Dispatcher:Assign(self,PlayerUnit,PlayerName) -end -self:GetMission():__Start(1) -self:__Goal(-10,PlayerUnit,PlayerName) -self:SetMenu() -self:F({"--> Task Assigned",TaskName=self:GetName(),Mission=self:GetMission():GetName()}) -self:F({"--> Task Player Names",PlayerNames=PlayerNames}) -end -end -function TASK:onenterSuccess(From,Event,To) -self:F({"<-> Task Replanned",TaskName=self:GetName(),Mission=self:GetMission():GetName()}) -self:F({"<-> Task Player Names",PlayerNames=self:GetPlayerNames()}) -self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." is successful! Good job!") -self:UnAssignFromGroups() -self:GetMission():__MissionGoals(1) -end -function TASK:onenterAborted(From,Event,To) -self:F({"<-- Task Aborted",TaskName=self:GetName(),Mission=self:GetMission():GetName()}) -self:F({"<-- Task Player Names",PlayerNames=self:GetPlayerNames()}) -if From~="Aborted"then -self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." has been aborted! Task may be replanned.") -self:__Replan(5) -self:SetMenu() -end -end -function TASK:onenterCancelled(From,Event,To) -self:F({"<-- Task Cancelled",TaskName=self:GetName(),Mission=self:GetMission():GetName()}) -self:F({"<-- Player Names",PlayerNames=self:GetPlayerNames()}) -if From~="Cancelled"then -self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." has been cancelled! The tactical situation has changed.") -self:UnAssignFromGroups() -self:SetMenu() -end -end -function TASK:onafterReplan(From,Event,To) -self:F({"Task Replanned",TaskName=self:GetName(),Mission=self:GetMission():GetName()}) -self:F({"Task Player Names",PlayerNames=self:GetPlayerNames()}) -self:GetMission():GetCommandCenter():MessageToCoalition("Replanning Task "..self:GetName()..".") -self:SetMenu() -end -function TASK:onenterFailed(From,Event,To) -self:F({"Task Failed",TaskName=self:GetName(),Mission=self:GetMission():GetName()}) -self:F({"Task Player Names",PlayerNames=self:GetPlayerNames()}) -self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." has failed!") -self:UnAssignFromGroups() -end -function TASK:onstatechange(From,Event,To) -if self:IsTrace()then -end -if self.Scores[To]then -local Scoring=self:GetScoring() -if Scoring then -self:F({self.Scores[To].ScoreText,self.Scores[To].Score}) -Scoring:_AddMissionScore(self.Mission,self.Scores[To].ScoreText,self.Scores[To].Score) -end -end -end -function TASK:onenterPlanned(From,Event,To) -if not self.TimeOut==0 then -self.__TimeOut(self.TimeOut) -end -end -function TASK:onbeforeTimeOut(From,Event,To) -if From=="Planned"then -self:RemoveMenu() -return true -end -return false -end -do -function TASK:SetGoal(Goal) -self.Goal=Goal -end -function TASK:GetGoal() -return self.Goal -end -function TASK:SetDispatcher(Dispatcher) -self.Dispatcher=Dispatcher -end -function TASK:SetDetection(Detection,DetectedItem) -self:F({DetectedItem,Detection}) -self.Detection=Detection -self.DetectedItem=DetectedItem -end -end -do -function TASK:ReportSummary(ReportGroup) -self:UpdateTaskInfo(self.DetectedItem) -local Report=REPORT:New() -Report:Add("Task "..self:GetName()) -Report:Add("State: <"..self:GetState()..">") -self.TaskInfo:Report(Report,"S",ReportGroup,self) -return Report:Text(', ') -end -function TASK:ReportOverview(ReportGroup) -self:UpdateTaskInfo(self.DetectedItem) -local TaskName=self:GetName() -local Report=REPORT:New() -self.TaskInfo:Report(Report,"O",ReportGroup,self) -return Report:Text() -end -function TASK:GetPlayerCount() -local PlayerCount=0 -for TaskGroupID,PlayerGroup in pairs(self:GetGroups():GetSet())do -local PlayerGroup=PlayerGroup -if PlayerGroup:IsAlive()==true then -if self:IsGroupAssigned(PlayerGroup)then -local PlayerNames=PlayerGroup:GetPlayerNames() -PlayerCount=PlayerCount+((PlayerNames)and#PlayerNames or 0) -end -end -end -return PlayerCount -end -function TASK:GetPlayerNames() -local PlayerNameMap={} -for TaskGroupID,PlayerGroup in pairs(self:GetGroups():GetSet())do -local PlayerGroup=PlayerGroup -if PlayerGroup:IsAlive()==true then -if self:IsGroupAssigned(PlayerGroup)then -local PlayerNames=PlayerGroup:GetPlayerNames() -for PlayerNameID,PlayerName in pairs(PlayerNames or{})do -PlayerNameMap[PlayerName]=PlayerGroup -end -end -end -end -return PlayerNameMap -end -function TASK:ReportDetails(ReportGroup) -self:UpdateTaskInfo(self.DetectedItem) -local Report=REPORT:New():SetIndent(3) -local Name=self:GetName() -local Status="<"..self:GetState()..">" -Report:Add("Task "..Name.." - "..Status.." - Detailed Report") -local PlayerNames=self:GetPlayerNames() -local PlayerReport=REPORT:New() -for PlayerName,PlayerGroup in pairs(PlayerNames)do -PlayerReport:Add("Players group "..PlayerGroup:GetCallsign()..": "..PlayerName) -end -local Players=PlayerReport:Text() -if Players~=""then -Report:AddIndent("Players assigned:","-") -Report:AddIndent(Players) -end -self.TaskInfo:Report(Report,"D",ReportGroup,self) -return Report:Text() -end -end -do -function TASK:AddProgress(PlayerName,ProgressText,ProgressTime,ProgressPoints) -self.TaskProgress=self.TaskProgress or{} -self.TaskProgress[ProgressTime]=self.TaskProgress[ProgressTime]or{} -self.TaskProgress[ProgressTime].PlayerName=PlayerName -self.TaskProgress[ProgressTime].ProgressText=ProgressText -self.TaskProgress[ProgressTime].ProgressPoints=ProgressPoints -self:GetMission():AddPlayerName(PlayerName) -return self -end -function TASK:GetPlayerProgress(PlayerName) -local ProgressPlayer=0 -for ProgressTime,ProgressData in pairs(self.TaskProgress)do -if PlayerName==ProgressData.PlayerName then -ProgressPlayer=ProgressPlayer+ProgressData.ProgressPoints -end -end -return ProgressPlayer -end -function TASK:SetScoreOnProgress(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","AccountPlayer","Player "..PlayerName.." has achieved progress.",Score) -return self -end -function TASK:SetScoreOnSuccess(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success","The task is a success!",Score) -return self -end -function TASK:SetScoreOnFail(PlayerName,Penalty,TaskUnit) -self:F({PlayerName,Penalty,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed","The task is a failure!",Penalty) -return self -end -end -do -function TASK:InitTaskControlMenu(TaskUnit) -self.TaskControlMenuTime=timer.getTime() -return self.TaskControlMenuTime -end -function TASK:GetTaskControlMenu(TaskUnit,TaskName) -TaskName=TaskName or"" -local TaskGroup=TaskUnit:GetGroup() -local TaskPlayerCount=TaskGroup:GetPlayerCount() -if TaskPlayerCount<=1 then -self.TaskControlMenu=MENU_GROUP:New(TaskUnit:GetGroup(),"Task "..self:GetName().." control"):SetTime(self.TaskControlMenuTime) -else -self.TaskControlMenu=MENU_GROUP:New(TaskUnit:GetGroup(),"Task "..self:GetName().." control for "..TaskUnit:GetPlayerName()):SetTime(self.TaskControlMenuTime) -end -return self.TaskControlMenu -end -function TASK:RemoveTaskControlMenu(TaskUnit) -if self.TaskControlMenu then -self.TaskControlMenu:Remove() -self.TaskControlMenu=nil -end -end -function TASK:RefreshTaskControlMenu(TaskUnit,MenuTime,MenuTag) -if self.TaskControlMenu then -self.TaskControlMenu:Remove(MenuTime,MenuTag) -end -end -end -TASKINFO={ -ClassName="TASKINFO", -} -TASKINFO.Detail="" -function TASKINFO:New(Task) -local self=BASE:Inherit(self,BASE:New()) -self.Task=Task -self.VolatileInfo=SET_BASE:New() -self.PersistentInfo=SET_BASE:New() -self.Info=self.VolatileInfo -return self -end -function TASKINFO:AddInfo(Key,Data,Order,Detail,Keep,ShowKey,Type) -self.VolatileInfo:Add(Key,{Data=Data,Order=Order,Detail=Detail,ShowKey=ShowKey,Type=Type}) -if Keep==true then -self.PersistentInfo:Add(Key,{Data=Data,Order=Order,Detail=Detail,ShowKey=ShowKey,Type=Type}) -end -return self -end -function TASKINFO:GetInfo(Key) -local Object=self:Get(Key) -return Object.Data,Object.Order,Object.Detail -end -function TASKINFO:GetData(Key) -local Object=self.Info:Get(Key) -return Object and Object.Data -end -function TASKINFO:AddText(Key,Text,Order,Detail,Keep) -self:AddInfo(Key,Text,Order,Detail,Keep) -return self -end -function TASKINFO:AddTaskName(Order,Detail,Keep) -self:AddInfo("TaskName",self.Task:GetName(),Order,Detail,Keep) -return self -end -function TASKINFO:AddCoordinate(Coordinate,Order,Detail,Keep,ShowKey,Name) -self:AddInfo(Name or"Coordinate",Coordinate,Order,Detail,Keep,ShowKey,"Coordinate") -return self -end -function TASKINFO:GetCoordinate(Name) -return self:GetData(Name or"Coordinate") -end -function TASKINFO:AddCoordinates(Coordinates,Order,Detail,Keep) -self:AddInfo("Coordinates",Coordinates,Order,Detail,Keep) -return self -end -function TASKINFO:AddThreat(ThreatText,ThreatLevel,Order,Detail,Keep) -self:AddInfo("Threat"," ["..string.rep("■",ThreatLevel)..string.rep("□",10-ThreatLevel).."]:"..ThreatText,Order,Detail,Keep) -return self -end -function TASKINFO:GetThreat() -self:GetInfo("Threat") -return self -end -function TASKINFO:AddTargetCount(TargetCount,Order,Detail,Keep) -self:AddInfo("Counting",string.format("%d",TargetCount),Order,Detail,Keep) -return self -end -function TASKINFO:AddTargets(TargetCount,TargetTypes,Order,Detail,Keep) -self:AddInfo("Targets",string.format("%d of %s",TargetCount,TargetTypes),Order,Detail,Keep) -return self -end -function TASKINFO:GetTargets() -self:GetInfo("Targets") -return self -end -function TASKINFO:AddQFEAtCoordinate(Coordinate,Order,Detail,Keep) -self:AddInfo("QFE",Coordinate,Order,Detail,Keep) -return self -end -function TASKINFO:AddTemperatureAtCoordinate(Coordinate,Order,Detail,Keep) -self:AddInfo("Temperature",Coordinate,Order,Detail,Keep) -return self -end -function TASKINFO:AddWindAtCoordinate(Coordinate,Order,Detail,Keep) -self:AddInfo("Wind",Coordinate,Order,Detail,Keep) -return self -end -function TASKINFO:AddCargo(Cargo,Order,Detail,Keep) -self:AddInfo("Cargo",Cargo,Order,Detail,Keep) -return self -end -function TASKINFO:AddCargoSet(SetCargo,Order,Detail,Keep) -local CargoReport=REPORT:New() -CargoReport:Add("") -SetCargo:ForEachCargo( -function(Cargo) -CargoReport:Add(string.format(' - %s (%s) %s - status %s ',Cargo:GetName(),Cargo:GetType(),Cargo:GetTransportationMethod(),Cargo:GetCurrentState())) -end -) -self:AddInfo("Cargo",CargoReport:Text(),Order,Detail,Keep) -return self -end -function TASKINFO:Report(Report,Detail,ReportGroup,Task) -local Line=0 -local LineReport=REPORT:New() -if not self.Task:IsStatePlanned()and not self.Task:IsStateAssigned()then -self.Info=self.PersistentInfo -end -for Key,Data in UTILS.spairs(self.Info.Set,function(t,a,b)return t[a].Order0 then -local TargetSetUnit=SET_UNIT:New() -TargetSetUnit:SetDatabase(DetectedSet) -TargetSetUnit:FilterHasSEAD() -TargetSetUnit:FilterOnce() -return TargetSetUnit -end -return nil -end -function TASK_A2G_DISPATCHER:EvaluateCAS(DetectedItem) -self:F({DetectedItem.ItemID}) -local DetectedSet=DetectedItem.Set -local DetectedZone=DetectedItem.Zone -local GroundUnitCount=DetectedSet:HasGroundUnits() -local FriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -local RadarCount=DetectedSet:HasSEAD() -if RadarCount==0 and GroundUnitCount>0 and FriendliesNearBy==true then -local TargetSetUnit=SET_UNIT:New() -TargetSetUnit:SetDatabase(DetectedSet) -TargetSetUnit:FilterOnce() -return TargetSetUnit -end -return nil -end -function TASK_A2G_DISPATCHER:EvaluateBAI(DetectedItem,FriendlyCoalition) -self:F({DetectedItem.ItemID}) -local DetectedSet=DetectedItem.Set -local DetectedZone=DetectedItem.Zone -local GroundUnitCount=DetectedSet:HasGroundUnits() -local FriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT) -local RadarCount=DetectedSet:HasSEAD() -if RadarCount==0 and GroundUnitCount>0 and FriendliesNearBy==false then -local TargetSetUnit=SET_UNIT:New() -TargetSetUnit:SetDatabase(DetectedSet) -TargetSetUnit:FilterOnce() -return TargetSetUnit -end -return nil -end -function TASK_A2G_DISPATCHER:RemoveTask(TaskIndex) -self.Mission:RemoveTask(self.Tasks[TaskIndex]) -self.Tasks[TaskIndex]=nil -end -function TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission,Task,TaskIndex,DetectedItemChanged) -if Task then -if(Task:IsStatePlanned()and DetectedItemChanged==true)or Task:IsStateCancelled()then -self:RemoveTask(TaskIndex) -end -end -return Task -end -function TASK_A2G_DISPATCHER:ProcessDetected(Detection) -self:F() -local AreaMsg={} -local TaskMsg={} -local ChangeMsg={} -local Mission=self.Mission -if Mission:IsIDLE()or Mission:IsENGAGED()then -local TaskReport=REPORT:New() -for TaskIndex,TaskData in pairs(self.Tasks)do -local Task=TaskData -if Task:IsStatePlanned()then -local DetectedItem=Detection:GetDetectedItemByIndex(TaskIndex) -if not DetectedItem then -local TaskText=Task:GetName() -for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do -if self.FlashNewTask then -Mission:GetCommandCenter():MessageToGroup(string.format("Obsolete A2G task %s for %s removed.",TaskText,Mission:GetShortText()),TaskGroup) -end -end -Task=self:RemoveTask(TaskIndex) -end -end -end -for DetectedItemID,DetectedItem in pairs(Detection:GetDetectedItems())do -local DetectedItem=DetectedItem -local DetectedSet=DetectedItem.Set -local DetectedZone=DetectedItem.Zone -local DetectedItemID=DetectedItem.ID -local TaskIndex=DetectedItem.Index -local DetectedItemChanged=DetectedItem.Changed -self:F({DetectedItemChanged=DetectedItemChanged,DetectedItemID=DetectedItemID,TaskIndex=TaskIndex}) -local Task=self.Tasks[TaskIndex] -if Task then -if Task:IsStateAssigned()then -if DetectedItemChanged==true then -local TargetsReport=REPORT:New() -local TargetSetUnit=self:EvaluateSEAD(DetectedItem) -if TargetSetUnit then -if Task:IsInstanceOf(TASK_A2G_SEAD)then -Task:SetTargetSetUnit(TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -TargetsReport:Add(Detection:GetChangeText(DetectedItem)) -else -Task:Cancel() -end -else -local TargetSetUnit=self:EvaluateCAS(DetectedItem) -if TargetSetUnit then -if Task:IsInstanceOf(TASK_A2G_CAS)then -Task:SetTargetSetUnit(TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -TargetsReport:Add(Detection:GetChangeText(DetectedItem)) -else -Task:Cancel() -Task=self:RemoveTask(TaskIndex) -end -else -local TargetSetUnit=self:EvaluateBAI(DetectedItem) -if TargetSetUnit then -if Task:IsInstanceOf(TASK_A2G_BAI)then -Task:SetTargetSetUnit(TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -TargetsReport:Add(Detection:GetChangeText(DetectedItem)) -else -Task:Cancel() -Task=self:RemoveTask(TaskIndex) -end -end -end -end -for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do -local TargetsText=TargetsReport:Text(", ") -if(Mission:IsGroupAssigned(TaskGroup))and TargetsText~=""and self.FlashNewTask then -Mission:GetCommandCenter():MessageToGroup(string.format("Task %s has change of targets:\n %s",Task:GetName(),TargetsText),TaskGroup) -end -end -end -end -end -if Task then -if Task:IsStatePlanned()then -if DetectedItemChanged==true then -if Task:IsInstanceOf(TASK_A2G_SEAD)then -local TargetSetUnit=self:EvaluateSEAD(DetectedItem) -if TargetSetUnit then -Task:SetTargetSetUnit(TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -else -Task:Cancel() -Task=self:RemoveTask(TaskIndex) -end -else -if Task:IsInstanceOf(TASK_A2G_CAS)then -local TargetSetUnit=self:EvaluateCAS(DetectedItem) -if TargetSetUnit then -Task:SetTargetSetUnit(TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -else -Task:Cancel() -Task=self:RemoveTask(TaskIndex) -end -else -if Task:IsInstanceOf(TASK_A2G_BAI)then -local TargetSetUnit=self:EvaluateBAI(DetectedItem) -if TargetSetUnit then -Task:SetTargetSetUnit(TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -else -Task:Cancel() -Task=self:RemoveTask(TaskIndex) -end -else -Task:Cancel() -Task=self:RemoveTask(TaskIndex) -end -end -end -end -end -end -if not Task then -local TargetSetUnit=self:EvaluateSEAD(DetectedItem) -if TargetSetUnit then -Task=TASK_A2G_SEAD:New(Mission,self.SetGroup,string.format("SEAD.%03d",DetectedItemID),TargetSetUnit) -DetectedItem.DesignateMenuName=string.format("SEAD.%03d",DetectedItemID) -Task:SetDetection(Detection,DetectedItem) -end -if not Task then -local TargetSetUnit=self:EvaluateCAS(DetectedItem) -if TargetSetUnit then -Task=TASK_A2G_CAS:New(Mission,self.SetGroup,string.format("CAS.%03d",DetectedItemID),TargetSetUnit) -DetectedItem.DesignateMenuName=string.format("CAS.%03d",DetectedItemID) -Task:SetDetection(Detection,DetectedItem) -end -if not Task then -local TargetSetUnit=self:EvaluateBAI(DetectedItem,self.Mission:GetCommandCenter():GetPositionable():GetCoalition()) -if TargetSetUnit then -Task=TASK_A2G_BAI:New(Mission,self.SetGroup,string.format("BAI.%03d",DetectedItemID),TargetSetUnit) -DetectedItem.DesignateMenuName=string.format("BAI.%03d",DetectedItemID) -Task:SetDetection(Detection,DetectedItem) -end -end -end -if Task then -self.Tasks[TaskIndex]=Task -Task:SetTargetZone(DetectedZone) -Task:SetDispatcher(self) -Task:UpdateTaskInfo(DetectedItem) -Mission:AddTask(Task) -function Task.OnEnterSuccess(Task,From,Event,To) -self:Success(Task) -end -function Task.OnEnterCancelled(Task,From,Event,To) -self:Cancelled(Task) -end -function Task.OnEnterFailed(Task,From,Event,To) -self:Failed(Task) -end -function Task.OnEnterAborted(Task,From,Event,To) -self:Aborted(Task) -end -TaskReport:Add(Task:GetName()) -else -self:F("This should not happen") -end -end -Detection:AcceptChanges(DetectedItem) -end -Mission:GetCommandCenter():SetMenu() -local TaskText=TaskReport:Text(", ") -for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do -if(not Mission:IsGroupAssigned(TaskGroup))and TaskText~=""and self.FlashNewTask then -Mission:GetCommandCenter():MessageToGroup(string.format("%s has tasks %s. Subscribe to a task using the radio menu.",Mission:GetShortText(),TaskText),TaskGroup) -end -end -end -return true -end -end -do -TASK_A2G={ -ClassName="TASK_A2G", -} -function TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskType,TaskBriefing) -local self=BASE:Inherit(self,TASK:New(Mission,SetGroup,TaskName,TaskType,TaskBriefing)) -self:F() -self.TargetSetUnit=TargetSetUnit -self.TaskType=TaskType -local Fsm=self:GetUnitProcess() -Fsm:AddTransition("Assigned","RouteToRendezVous","RoutingToRendezVous") -Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousPoint",ACT_ROUTE_POINT:New(),{Arrived="ArriveAtRendezVous"}) -Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousZone",ACT_ROUTE_ZONE:New(),{Arrived="ArriveAtRendezVous"}) -Fsm:AddTransition({"Arrived","RoutingToRendezVous"},"ArriveAtRendezVous","ArrivedAtRendezVous") -Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"Engage","Engaging") -Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"HoldAtRendezVous","HoldingAtRendezVous") -Fsm:AddProcess("Engaging","Account",ACT_ACCOUNT_DEADS:New(),{}) -Fsm:AddTransition("Engaging","RouteToTarget","Engaging") -Fsm:AddProcess("Engaging","RouteToTargetZone",ACT_ROUTE_ZONE:New(),{}) -Fsm:AddProcess("Engaging","RouteToTargetPoint",ACT_ROUTE_POINT:New(),{}) -Fsm:AddTransition("Engaging","RouteToTargets","Engaging") -Fsm:AddTransition("Rejected","Reject","Aborted") -Fsm:AddTransition("Failed","Fail","Failed") -function Fsm:onafterAssigned(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -self:RouteToRendezVous() -end -function Fsm:onafterRouteToRendezVous(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Task:GetRendezVousZone(TaskUnit)then -self:__RouteToRendezVousZone(0.1) -else -if Task:GetRendezVousCoordinate(TaskUnit)then -self:__RouteToRendezVousPoint(0.1) -else -self:__ArriveAtRendezVous(0.1) -end -end -end -function Fsm:OnAfterArriveAtRendezVous(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -self:__Engage(0.1) -end -function Fsm:onafterEngage(TaskUnit,Task) -self:F({self}) -self:__Account(0.1) -self:__RouteToTarget(0.1) -self:__RouteToTargets(-10) -end -function Fsm:onafterRouteToTarget(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Task:GetTargetZone(TaskUnit)then -self:__RouteToTargetZone(0.1) -else -local TargetUnit=Task.TargetSetUnit:GetFirst() -if TargetUnit then -local Coordinate=TargetUnit:GetPointVec3() -self:T({TargetCoordinate=Coordinate,Coordinate:GetX(),Coordinate:GetY(),Coordinate:GetZ()}) -Task:SetTargetCoordinate(Coordinate,TaskUnit) -end -self:__RouteToTargetPoint(0.1) -end -end -function Fsm:onafterRouteToTargets(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -local TargetUnit=Task.TargetSetUnit:GetFirst() -if TargetUnit then -Task:SetTargetCoordinate(TargetUnit:GetCoordinate(),TaskUnit) -end -self:__RouteToTargets(-10) -end -return self -end -function TASK_A2G:SetTargetSetUnit(TargetSetUnit) -self.TargetSetUnit=TargetSetUnit -end -function TASK_A2G:GetPlannedMenuText() -return self:GetStateString().." - "..self:GetTaskName().." ( "..self.TargetSetUnit:GetUnitTypesText().." )" -end -function TASK_A2G:SetRendezVousCoordinate(RendezVousCoordinate,RendezVousRange,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint") -ActRouteRendezVous:SetCoordinate(RendezVousCoordinate) -ActRouteRendezVous:SetRange(RendezVousRange) -end -function TASK_A2G:GetRendezVousCoordinate(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint") -return ActRouteRendezVous:GetCoordinate(),ActRouteRendezVous:GetRange() -end -function TASK_A2G:SetRendezVousZone(RendezVousZone,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone") -ActRouteRendezVous:SetZone(RendezVousZone) -end -function TASK_A2G:GetRendezVousZone(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone") -return ActRouteRendezVous:GetZone() -end -function TASK_A2G:SetTargetCoordinate(TargetCoordinate,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint") -ActRouteTarget:SetCoordinate(TargetCoordinate) -end -function TASK_A2G:GetTargetCoordinate(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint") -return ActRouteTarget:GetCoordinate() -end -function TASK_A2G:SetTargetZone(TargetZone,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone") -ActRouteTarget:SetZone(TargetZone) -end -function TASK_A2G:GetTargetZone(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone") -return ActRouteTarget:GetZone() -end -function TASK_A2G:SetGoalTotal() -self.GoalTotal=self.TargetSetUnit:Count() -end -function TASK_A2G:GetGoalTotal() -return self.GoalTotal -end -function TASK_A2G:ReportOrder(ReportGroup) -self:UpdateTaskInfo(self.DetectedItem) -local Coordinate=self.TaskInfo:GetData("Coordinate") -local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) -return Distance -end -function TASK_A2G:onafterGoal(TaskUnit,From,Event,To) -local TargetSetUnit=self.TargetSetUnit -if TargetSetUnit:Count()==0 then -self:Success() -end -self:__Goal(-10) -end -function TASK_A2G:UpdateTaskInfo(DetectedItem) -if self:IsStatePlanned()or self:IsStateAssigned()then -local TargetCoordinate=DetectedItem and self.Detection:GetDetectedItemCoordinate(DetectedItem)or self.TargetSetUnit:GetFirst():GetCoordinate() -self.TaskInfo:AddTaskName(0,"MSOD") -self.TaskInfo:AddCoordinate(TargetCoordinate,1,"SOD") -local ThreatLevel,ThreatText -if DetectedItem then -ThreatLevel,ThreatText=self.Detection:GetDetectedItemThreatLevel(DetectedItem) -else -ThreatLevel,ThreatText=self.TargetSetUnit:CalculateThreatLevelA2G() -end -self.TaskInfo:AddThreat(ThreatText,ThreatLevel,10,"MOD",true) -if self.Detection then -local DetectedItemsCount=self.TargetSetUnit:Count() -local ReportTypes=REPORT:New() -local TargetTypes={} -for TargetUnitName,TargetUnit in pairs(self.TargetSetUnit:GetSet())do -local TargetType=self.Detection:GetDetectedUnitTypeName(TargetUnit) -if not TargetTypes[TargetType]then -TargetTypes[TargetType]=TargetType -ReportTypes:Add(TargetType) -end -end -self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true) -self.TaskInfo:AddTargets(DetectedItemsCount,ReportTypes:Text(", "),20,"D",true) -else -local DetectedItemsCount=self.TargetSetUnit:Count() -local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() -self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true) -self.TaskInfo:AddTargets(DetectedItemsCount,DetectedItemsTypes,20,"D",true) -end -self.TaskInfo:AddQFEAtCoordinate(TargetCoordinate,30,"MOD") -self.TaskInfo:AddTemperatureAtCoordinate(TargetCoordinate,31,"MD") -self.TaskInfo:AddWindAtCoordinate(TargetCoordinate,32,"MD") -end -end -function TASK_A2G:GetAutoAssignPriority(AutoAssignMethod,CommandCenter,TaskGroup) -if AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Random then -return math.random(1,9) -elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Distance then -local Coordinate=self.TaskInfo:GetData("Coordinate") -local Distance=Coordinate:Get2DDistance(CommandCenter:GetPositionable():GetCoordinate()) -self:F({Distance=Distance}) -return math.floor(Distance) -elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Priority then -return 1 -end -return 0 -end -end -do -TASK_A2G_SEAD={ -ClassName="TASK_A2G_SEAD", -} -function TASK_A2G_SEAD:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing) -local self=BASE:Inherit(self,TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,"SEAD",TaskBriefing)) -self:F() -Mission:AddTask(self) -self:SetBriefing( -TaskBriefing or -"Execute a Suppression of Enemy Air Defenses." -) -return self -end -function TASK_A2G_SEAD:SetScoreOnProgress(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has SEADed a target.",Score) -return self -end -function TASK_A2G_SEAD:SetScoreOnSuccess(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success","All radar emitting targets have been successfully SEADed!",Score) -return self -end -function TASK_A2G_SEAD:SetScoreOnFail(PlayerName,Penalty,TaskUnit) -self:F({PlayerName,Penalty,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed","The SEADing has failed!",Penalty) -return self -end -end -do -TASK_A2G_BAI={ -ClassName="TASK_A2G_BAI", -} -function TASK_A2G_BAI:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing) -local self=BASE:Inherit(self,TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,"BAI",TaskBriefing)) -self:F() -Mission:AddTask(self) -self:SetBriefing( -TaskBriefing or -"Execute a Battlefield Air Interdiction of a group of enemy targets." -) -return self -end -function TASK_A2G_BAI:SetScoreOnProgress(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has destroyed a target in Battlefield Air Interdiction (BAI).",Score) -return self -end -function TASK_A2G_BAI:SetScoreOnSuccess(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success","All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!",Score) -return self -end -function TASK_A2G_BAI:SetScoreOnFail(PlayerName,Penalty,TaskUnit) -self:F({PlayerName,Penalty,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed","The Battlefield Air Interdiction (BAI) has failed!",Penalty) -return self -end -end -do -TASK_A2G_CAS={ -ClassName="TASK_A2G_CAS", -} -function TASK_A2G_CAS:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing) -local self=BASE:Inherit(self,TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,"CAS",TaskBriefing)) -self:F() -Mission:AddTask(self) -self:SetBriefing( -TaskBriefing or -"Execute a Close Air Support for a group of enemy targets. ".. -"Beware of friendlies at the vicinity! " -) -return self -end -function TASK_A2G_CAS:SetScoreOnProgress(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has destroyed a target in Close Air Support (CAS).",Score) -return self -end -function TASK_A2G_CAS:SetScoreOnSuccess(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success","All targets have been successfully destroyed! The Close Air Support (CAS) was a success!",Score) -return self -end -function TASK_A2G_CAS:SetScoreOnFail(PlayerName,Penalty,TaskUnit) -self:F({PlayerName,Penalty,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed","The Close Air Support (CAS) has failed!",Penalty) -return self -end -end -do -TASK_A2A_DISPATCHER={ -ClassName="TASK_A2A_DISPATCHER", -Mission=nil, -Detection=nil, -Tasks={}, -SweepZones={}, -} -function TASK_A2A_DISPATCHER:New(Mission,SetGroup,Detection) -local self=BASE:Inherit(self,DETECTION_MANAGER:New(SetGroup,Detection)) -self.Detection=Detection -self.Mission=Mission -self.FlashNewTask=false -self.Detection:FilterCategories(Unit.Category.AIRPLANE,Unit.Category.HELICOPTER) -self.Detection:InitDetectRadar(true) -self.Detection:SetRefreshTimeInterval(30) -self:AddTransition("Started","Assign","Started") -self:__Start(5) -return self -end -function TASK_A2A_DISPATCHER:SetEngageRadius(EngageRadius) -self.Detection:SetFriendliesRange(EngageRadius or 100000) -return self -end -function TASK_A2A_DISPATCHER:SetSendMessages(onoff) -self.FlashNewTask=onoff -end -function TASK_A2A_DISPATCHER:EvaluateINTERCEPT(DetectedItem) -self:F({DetectedItem.ItemID}) -local DetectedSet=DetectedItem.Set -local DetectedZone=DetectedItem.Zone -if DetectedItem.IsDetected==true then -local TargetSetUnit=SET_UNIT:New() -TargetSetUnit:SetDatabase(DetectedSet) -TargetSetUnit:FilterOnce() -return TargetSetUnit -end -return nil -end -function TASK_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem) -self:F({DetectedItem.ItemID}) -local DetectedSet=DetectedItem.Set -local DetectedZone=DetectedItem.Zone -if DetectedItem.IsDetected==false then -local TargetSetUnit=SET_UNIT:New() -TargetSetUnit:SetDatabase(DetectedSet) -TargetSetUnit:FilterOnce() -return TargetSetUnit -end -return nil -end -function TASK_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem) -self:F({DetectedItem.ItemID}) -local DetectedSet=DetectedItem.Set -local DetectedZone=DetectedItem.Zone -local PlayersCount,PlayersReport=self:GetPlayerFriendliesNearBy(DetectedItem) -if PlayersCount>0 and DetectedItem.IsDetected==true then -local TargetSetUnit=SET_UNIT:New() -TargetSetUnit:SetDatabase(DetectedSet) -TargetSetUnit:FilterOnce() -return TargetSetUnit -end -return nil -end -function TASK_A2A_DISPATCHER:EvaluateRemoveTask(Mission,Task,Detection,DetectedItem,DetectedItemIndex,DetectedItemChanged) -if Task then -if Task:IsStatePlanned()then -local TaskName=Task:GetName() -local TaskType=TaskName:match("(%u+)%.%d+") -self:T2({TaskType=TaskType}) -local Remove=false -local IsPlayers=Detection:IsPlayersNearBy(DetectedItem) -if TaskType=="ENGAGE"then -if IsPlayers==false then -Remove=true -end -end -if TaskType=="INTERCEPT"then -if IsPlayers==true then -Remove=true -end -if DetectedItem.IsDetected==false then -Remove=true -end -end -if TaskType=="SWEEP"then -if DetectedItem.IsDetected==true then -Remove=true -end -end -local DetectedSet=DetectedItem.Set -if DetectedSet:Count()==0 then -Remove=true -end -if DetectedItemChanged==true or Remove then -Task=self:RemoveTask(DetectedItemIndex) -end -end -end -return Task -end -function TASK_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem) -local DetectedSet=DetectedItem.Set -local FriendlyUnitsNearBy=self.Detection:GetFriendliesNearBy(DetectedItem,Unit.Category.AIRPLANE) -local FriendlyTypes={} -local FriendliesCount=0 -if FriendlyUnitsNearBy then -local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G() -for FriendlyUnitName,FriendlyUnitData in pairs(FriendlyUnitsNearBy)do -local FriendlyUnit=FriendlyUnitData -if FriendlyUnit:IsAirPlane()then -local FriendlyUnitThreatLevel=FriendlyUnit:GetThreatLevel() -FriendliesCount=FriendliesCount+1 -local FriendlyType=FriendlyUnit:GetTypeName() -FriendlyTypes[FriendlyType]=FriendlyTypes[FriendlyType]and(FriendlyTypes[FriendlyType]+1)or 1 -if DetectedTreatLevel0 then -for FriendlyType,FriendlyTypeCount in pairs(FriendlyTypes)do -FriendlyTypesReport:Add(string.format("%d of %s",FriendlyTypeCount,FriendlyType)) -end -else -FriendlyTypesReport:Add("-") -end -return FriendliesCount,FriendlyTypesReport -end -function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem) -local DetectedSet=DetectedItem.Set -local PlayersNearBy=self.Detection:GetPlayersNearBy(DetectedItem) -local PlayerTypes={} -local PlayersCount=0 -if PlayersNearBy then -local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G() -for PlayerUnitName,PlayerUnitData in pairs(PlayersNearBy)do -local PlayerUnit=PlayerUnitData -local PlayerName=PlayerUnit:GetPlayerName() -if PlayerUnit:IsAirPlane()and PlayerName~=nil then -local FriendlyUnitThreatLevel=PlayerUnit:GetThreatLevel() -PlayersCount=PlayersCount+1 -local PlayerType=PlayerUnit:GetTypeName() -PlayerTypes[PlayerName]=PlayerType -if DetectedTreatLevel0 then -for PlayerName,PlayerType in pairs(PlayerTypes)do -PlayerTypesReport:Add(string.format('"%s" in %s',PlayerName,PlayerType)) -end -else -PlayerTypesReport:Add("-") -end -return PlayersCount,PlayerTypesReport -end -function TASK_A2A_DISPATCHER:RemoveTask(TaskIndex) -self.Mission:RemoveTask(self.Tasks[TaskIndex]) -self.Tasks[TaskIndex]=nil -end -function TASK_A2A_DISPATCHER:ProcessDetected(Detection) -self:F() -local AreaMsg={} -local TaskMsg={} -local ChangeMsg={} -local Mission=self.Mission -if Mission:IsIDLE()or Mission:IsENGAGED()then -local TaskReport=REPORT:New() -for TaskIndex,TaskData in pairs(self.Tasks)do -local Task=TaskData -if Task:IsStatePlanned()then -local DetectedItem=Detection:GetDetectedItemByIndex(TaskIndex) -if not DetectedItem then -local TaskText=Task:GetName() -for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do -Mission:GetCommandCenter():MessageToGroup(string.format("Obsolete A2A task %s for %s removed.",TaskText,Mission:GetShortText()),TaskGroup) -end -Task=self:RemoveTask(TaskIndex) -end -end -end -for DetectedItemID,DetectedItem in pairs(Detection:GetDetectedItems())do -local DetectedItem=DetectedItem -local DetectedSet=DetectedItem.Set -local DetectedCount=DetectedSet:Count() -local DetectedZone=DetectedItem.Zone -local DetectedID=DetectedItem.ID -local TaskIndex=DetectedItem.Index -local DetectedItemChanged=DetectedItem.Changed -local Task=self.Tasks[TaskIndex] -Task=self:EvaluateRemoveTask(Mission,Task,Detection,DetectedItem,TaskIndex,DetectedItemChanged) -if not Task and DetectedCount>0 then -local TargetSetUnit=self:EvaluateENGAGE(DetectedItem) -if TargetSetUnit then -Task=TASK_A2A_ENGAGE:New(Mission,self.SetGroup,string.format("ENGAGE.%03d",DetectedID),TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -else -local TargetSetUnit=self:EvaluateINTERCEPT(DetectedItem) -if TargetSetUnit then -Task=TASK_A2A_INTERCEPT:New(Mission,self.SetGroup,string.format("INTERCEPT.%03d",DetectedID),TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -else -local TargetSetUnit=self:EvaluateSWEEP(DetectedItem) -if TargetSetUnit then -Task=TASK_A2A_SWEEP:New(Mission,self.SetGroup,string.format("SWEEP.%03d",DetectedID),TargetSetUnit) -Task:SetDetection(Detection,DetectedItem) -Task:UpdateTaskInfo(DetectedItem) -end -end -end -if Task then -self.Tasks[TaskIndex]=Task -Task:SetTargetZone(DetectedZone,DetectedItem.Coordinate.y,DetectedItem.Coordinate.Heading) -Task:SetDispatcher(self) -Mission:AddTask(Task) -function Task.OnEnterSuccess(Task,From,Event,To) -self:Success(Task) -end -function Task.OnEnterCancelled(Task,From,Event,To) -self:Cancelled(Task) -end -function Task.OnEnterFailed(Task,From,Event,To) -self:Failed(Task) -end -function Task.OnEnterAborted(Task,From,Event,To) -self:Aborted(Task) -end -TaskReport:Add(Task:GetName()) -else -self:F("This should not happen") -end -end -if Task then -local FriendliesCount,FriendliesReport=self:GetFriendliesNearBy(DetectedItem,Unit.Category.AIRPLANE) -Task.TaskInfo:AddText("Friendlies",string.format("%d ( %s )",FriendliesCount,FriendliesReport:Text(",")),40,"MOD") -local PlayersCount,PlayersReport=self:GetPlayerFriendliesNearBy(DetectedItem) -Task.TaskInfo:AddText("Players",string.format("%d ( %s )",PlayersCount,PlayersReport:Text(",")),40,"MOD") -end -Detection:AcceptChanges(DetectedItem) -end -Mission:GetCommandCenter():SetMenu() -local TaskText=TaskReport:Text(", ") -for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do -if(not Mission:IsGroupAssigned(TaskGroup))and TaskText~=""and(self.FlashNewTask)then -Mission:GetCommandCenter():MessageToGroup(string.format("%s has tasks %s. Subscribe to a task using the radio menu.",Mission:GetShortText(),TaskText),TaskGroup) -end -end -end -return true -end -end -do -TASK_A2A={ -ClassName="TASK_A2A", -} -function TASK_A2A:New(Mission,SetAttack,TaskName,TargetSetUnit,TaskType,TaskBriefing) -local self=BASE:Inherit(self,TASK:New(Mission,SetAttack,TaskName,TaskType,TaskBriefing)) -self:F() -self.TargetSetUnit=TargetSetUnit -self.TaskType=TaskType -local Fsm=self:GetUnitProcess() -Fsm:AddTransition("Assigned","RouteToRendezVous","RoutingToRendezVous") -Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousPoint",ACT_ROUTE_POINT:New(),{Arrived="ArriveAtRendezVous"}) -Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousZone",ACT_ROUTE_ZONE:New(),{Arrived="ArriveAtRendezVous"}) -Fsm:AddTransition({"Arrived","RoutingToRendezVous"},"ArriveAtRendezVous","ArrivedAtRendezVous") -Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"Engage","Engaging") -Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"HoldAtRendezVous","HoldingAtRendezVous") -Fsm:AddProcess("Engaging","Account",ACT_ACCOUNT_DEADS:New(),{}) -Fsm:AddTransition("Engaging","RouteToTarget","Engaging") -Fsm:AddProcess("Engaging","RouteToTargetZone",ACT_ROUTE_ZONE:New(),{}) -Fsm:AddProcess("Engaging","RouteToTargetPoint",ACT_ROUTE_POINT:New(),{}) -Fsm:AddTransition("Engaging","RouteToTargets","Engaging") -Fsm:AddTransition("Rejected","Reject","Aborted") -Fsm:AddTransition("Failed","Fail","Failed") -function Fsm:OnLeaveAssigned(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -self:SelectAction() -end -function Fsm:onafterRouteToRendezVous(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Task:GetRendezVousZone(TaskUnit)then -self:__RouteToRendezVousZone(0.1) -else -if Task:GetRendezVousCoordinate(TaskUnit)then -self:__RouteToRendezVousPoint(0.1) -else -self:__ArriveAtRendezVous(0.1) -end -end -end -function Fsm:OnAfterArriveAtRendezVous(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -self:__Engage(0.1) -end -function Fsm:onafterEngage(TaskUnit,Task) -self:F({self}) -self:__Account(0.1) -self:__RouteToTarget(0.1) -self:__RouteToTargets(-10) -end -function Fsm:onafterRouteToTarget(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Task:GetTargetZone(TaskUnit)then -self:__RouteToTargetZone(0.1) -else -local TargetUnit=Task.TargetSetUnit:GetFirst() -if TargetUnit then -local Coordinate=TargetUnit:GetPointVec3() -self:T({TargetCoordinate=Coordinate,Coordinate:GetX(),Coordinate:GetAlt(),Coordinate:GetZ()}) -Task:SetTargetCoordinate(Coordinate,TaskUnit) -end -self:__RouteToTargetPoint(0.1) -end -end -function Fsm:onafterRouteToTargets(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -local TargetUnit=Task.TargetSetUnit:GetFirst() -if TargetUnit then -Task:SetTargetCoordinate(TargetUnit:GetCoordinate(),TaskUnit) -end -self:__RouteToTargets(-10) -end -return self -end -function TASK_A2A:SetTargetSetUnit(TargetSetUnit) -self.TargetSetUnit=TargetSetUnit -end -function TASK_A2A:GetPlannedMenuText() -return self:GetStateString().." - "..self:GetTaskName().." ( "..self.TargetSetUnit:GetUnitTypesText().." )" -end -function TASK_A2A:SetRendezVousCoordinate(RendezVousCoordinate,RendezVousRange,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint") -ActRouteRendezVous:SetCoordinate(RendezVousCoordinate) -ActRouteRendezVous:SetRange(RendezVousRange) -end -function TASK_A2A:GetRendezVousCoordinate(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint") -return ActRouteRendezVous:GetCoordinate(),ActRouteRendezVous:GetRange() -end -function TASK_A2A:SetRendezVousZone(RendezVousZone,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone") -ActRouteRendezVous:SetZone(RendezVousZone) -end -function TASK_A2A:GetRendezVousZone(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone") -return ActRouteRendezVous:GetZone() -end -function TASK_A2A:SetTargetCoordinate(TargetCoordinate,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint") -ActRouteTarget:SetCoordinate(TargetCoordinate) -end -function TASK_A2A:GetTargetCoordinate(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint") -return ActRouteTarget:GetCoordinate() -end -function TASK_A2A:SetTargetZone(TargetZone,Altitude,Heading,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone") -ActRouteTarget:SetZone(TargetZone,Altitude,Heading) -end -function TASK_A2A:GetTargetZone(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone") -return ActRouteTarget:GetZone() -end -function TASK_A2A:SetGoalTotal() -self.GoalTotal=self.TargetSetUnit:Count() -end -function TASK_A2A:GetGoalTotal() -return self.GoalTotal -end -function TASK_A2A:ReportOrder(ReportGroup) -self:UpdateTaskInfo(self.DetectedItem) -local Coordinate=self.TaskInfo:GetData("Coordinate") -local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) -return Distance -end -function TASK_A2A:onafterGoal(TaskUnit,From,Event,To) -local TargetSetUnit=self.TargetSetUnit -if TargetSetUnit:Count()==0 then -self:Success() -end -self:__Goal(-10) -end -function TASK_A2A:UpdateTaskInfo(DetectedItem) -if self:IsStatePlanned()or self:IsStateAssigned()then -local TargetCoordinate=DetectedItem and self.Detection:GetDetectedItemCoordinate(DetectedItem)or self.TargetSetUnit:GetFirst():GetCoordinate() -self.TaskInfo:AddTaskName(0,"MSOD") -self.TaskInfo:AddCoordinate(TargetCoordinate,1,"SOD") -local ThreatLevel,ThreatText -if DetectedItem then -ThreatLevel,ThreatText=self.Detection:GetDetectedItemThreatLevel(DetectedItem) -else -ThreatLevel,ThreatText=self.TargetSetUnit:CalculateThreatLevelA2G() -end -self.TaskInfo:AddThreat(ThreatText,ThreatLevel,10,"MOD",true) -if self.Detection then -local DetectedItemsCount=self.TargetSetUnit:Count() -local ReportTypes=REPORT:New() -local TargetTypes={} -for TargetUnitName,TargetUnit in pairs(self.TargetSetUnit:GetSet())do -local TargetType=self.Detection:GetDetectedUnitTypeName(TargetUnit) -if not TargetTypes[TargetType]then -TargetTypes[TargetType]=TargetType -ReportTypes:Add(TargetType) -end -end -self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true) -self.TaskInfo:AddTargets(DetectedItemsCount,ReportTypes:Text(", "),20,"D",true) -else -local DetectedItemsCount=self.TargetSetUnit:Count() -local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() -self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true) -self.TaskInfo:AddTargets(DetectedItemsCount,DetectedItemsTypes,20,"D",true) -end -end -end -function TASK_A2A:GetAutoAssignPriority(AutoAssignMethod,CommandCenter,TaskGroup) -if AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Random then -return math.random(1,9) -elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Distance then -local Coordinate=self.TaskInfo:GetData("Coordinate") -local Distance=Coordinate:Get2DDistance(CommandCenter:GetPositionable():GetCoordinate()) -return math.floor(Distance) -elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Priority then -return 1 -end -return 0 -end -end -do -TASK_A2A_INTERCEPT={ -ClassName="TASK_A2A_INTERCEPT", -} -function TASK_A2A_INTERCEPT:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing) -local self=BASE:Inherit(self,TASK_A2A:New(Mission,SetGroup,TaskName,TargetSetUnit,"INTERCEPT",TaskBriefing)) -self:F() -Mission:AddTask(self) -self:SetBriefing( -TaskBriefing or -"Intercept incoming intruders.\n" -) -return self -end -function TASK_A2A_INTERCEPT:SetScoreOnProgress(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has intercepted a target.",Score) -return self -end -function TASK_A2A_INTERCEPT:SetScoreOnSuccess(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success","All targets have been successfully intercepted!",Score) -return self -end -function TASK_A2A_INTERCEPT:SetScoreOnFail(PlayerName,Penalty,TaskUnit) -self:F({PlayerName,Penalty,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed","The intercept has failed!",Penalty) -return self -end -end -do -TASK_A2A_SWEEP={ -ClassName="TASK_A2A_SWEEP", -} -function TASK_A2A_SWEEP:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing) -local self=BASE:Inherit(self,TASK_A2A:New(Mission,SetGroup,TaskName,TargetSetUnit,"SWEEP",TaskBriefing)) -self:F() -Mission:AddTask(self) -self:SetBriefing( -TaskBriefing or -"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" -) -return self -end -function TASK_A2A_SWEEP:onafterGoal(TaskUnit,From,Event,To) -local TargetSetUnit=self.TargetSetUnit -if TargetSetUnit:Count()==0 then -self:Success() -end -self:__Goal(-10) -end -function TASK_A2A_SWEEP:SetScoreOnProgress(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has sweeped a target.",Score) -return self -end -function TASK_A2A_SWEEP:SetScoreOnSuccess(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success","All targets have been successfully sweeped!",Score) -return self -end -function TASK_A2A_SWEEP:SetScoreOnFail(PlayerName,Penalty,TaskUnit) -self:F({PlayerName,Penalty,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed","The sweep has failed!",Penalty) -return self -end -end -do -TASK_A2A_ENGAGE={ -ClassName="TASK_A2A_ENGAGE", -} -function TASK_A2A_ENGAGE:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing) -local self=BASE:Inherit(self,TASK_A2A:New(Mission,SetGroup,TaskName,TargetSetUnit,"ENGAGE",TaskBriefing)) -self:F() -Mission:AddTask(self) -self:SetBriefing( -TaskBriefing or -"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" -) -return self -end -function TASK_A2A_ENGAGE:SetScoreOnProgress(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has engaged and destroyed a target.",Score) -return self -end -function TASK_A2A_ENGAGE:SetScoreOnSuccess(PlayerName,Score,TaskUnit) -self:F({PlayerName,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success","All targets have been successfully engaged!",Score) -return self -end -function TASK_A2A_ENGAGE:SetScoreOnFail(PlayerName,Penalty,TaskUnit) -self:F({PlayerName,Penalty,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed","The target engagement has failed!",Penalty) -return self -end -end -do -TASK_CARGO={ -ClassName="TASK_CARGO", -} -function TASK_CARGO:New(Mission,SetGroup,TaskName,SetCargo,TaskType,TaskBriefing) -local self=BASE:Inherit(self,TASK:New(Mission,SetGroup,TaskName,TaskType,TaskBriefing)) -self:F({Mission,SetGroup,TaskName,SetCargo,TaskType}) -self.SetCargo=SetCargo -self.TaskType=TaskType -self.SmokeColor=SMOKECOLOR.Red -self.CargoItemCount={} -self.CargoLimit=10 -self.DeployZones={} -self:AddTransition("*","CargoDeployed","*") -self:AddTransition("*","CargoPickedUp","*") -local Fsm=self:GetUnitProcess() -Fsm:AddTransition({"Planned","Assigned","Cancelled","WaitingForCommand","ArrivedAtPickup","ArrivedAtDeploy","Boarded","UnBoarded","Loaded","UnLoaded","Landed","Boarding"},"SelectAction","*") -Fsm:AddTransition("*","RouteToPickup","RoutingToPickup") -Fsm:AddProcess("RoutingToPickup","RouteToPickupPoint",ACT_ROUTE_POINT:New(),{Arrived="ArriveAtPickup",Cancelled="CancelRouteToPickup"}) -Fsm:AddTransition("Arrived","ArriveAtPickup","ArrivedAtPickup") -Fsm:AddTransition("Cancelled","CancelRouteToPickup","Cancelled") -Fsm:AddTransition("*","RouteToDeploy","RoutingToDeploy") -Fsm:AddProcess("RoutingToDeploy","RouteToDeployZone",ACT_ROUTE_ZONE:New(),{Arrived="ArriveAtDeploy",Cancelled="CancelRouteToDeploy"}) -Fsm:AddTransition("Arrived","ArriveAtDeploy","ArrivedAtDeploy") -Fsm:AddTransition("Cancelled","CancelRouteToDeploy","Cancelled") -Fsm:AddTransition({"ArrivedAtPickup","ArrivedAtDeploy","Landing"},"Land","Landing") -Fsm:AddTransition("Landing","Landed","Landed") -Fsm:AddTransition("*","PrepareBoarding","AwaitBoarding") -Fsm:AddTransition("AwaitBoarding","Board","Boarding") -Fsm:AddTransition("Boarding","Boarded","Boarded") -Fsm:AddTransition("*","Load","Loaded") -Fsm:AddTransition("*","PrepareUnBoarding","AwaitUnBoarding") -Fsm:AddTransition("AwaitUnBoarding","UnBoard","UnBoarding") -Fsm:AddTransition("UnBoarding","UnBoarded","UnBoarded") -Fsm:AddTransition("*","Unload","Unloaded") -Fsm:AddTransition("*","Planned","Planned") -Fsm:AddTransition("Deployed","Success","Success") -Fsm:AddTransition("Rejected","Reject","Aborted") -Fsm:AddTransition("Failed","Fail","Failed") -function Fsm:OnAfterAssigned(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -self:SelectAction() -end -function Fsm:onafterSelectAction(TaskUnit,Task) -local TaskUnitName=TaskUnit:GetName() -local MenuTime=Task:InitTaskControlMenu(TaskUnit) -local MenuControl=Task:GetTaskControlMenu(TaskUnit) -Task.SetCargo:ForEachCargo( -function(Cargo) -if Cargo:IsAlive()then -local TaskGroup=TaskUnit:GetGroup() -if Cargo:IsUnLoaded()then -local CargoBayFreeWeight=TaskUnit:GetCargoBayFreeWeight() -local CargoWeight=Cargo:GetWeight() -self:F({CargoBayFreeWeight=CargoBayFreeWeight}) -if CargoBayFreeWeight>CargoWeight then -if Cargo:IsInReportRadius(TaskUnit:GetPointVec2())then -local NotInDeployZones=true -for DeployZoneName,DeployZone in pairs(Task.DeployZones)do -if Cargo:IsInZone(DeployZone)then -NotInDeployZones=false -end -end -if NotInDeployZones then -if not TaskUnit:InAir()then -if Cargo:CanBoard()==true then -if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then -Cargo:Report("Ready for boarding.","board",TaskUnit:GetGroup()) -local BoardMenu=MENU_GROUP:New(TaskGroup,"Board cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,BoardMenu,self.MenuBoardCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -else -Cargo:Report("Board at "..Cargo:GetCoordinate():ToString(TaskUnit:GetGroup().."."),"reporting",TaskUnit:GetGroup()) -end -else -if Cargo:CanLoad()==true then -if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then -Cargo:Report("Ready for loading.","load",TaskUnit:GetGroup()) -local LoadMenu=MENU_GROUP:New(TaskGroup,"Load cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,LoadMenu,self.MenuLoadCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -else -Cargo:Report("Load at "..Cargo:GetCoordinate():ToString(TaskUnit:GetGroup()).." within "..Cargo.NearRadius..".","reporting",TaskUnit:GetGroup()) -end -else -if Cargo:CanSlingload()==true then -if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then -Cargo:Report("Ready for sling loading.","slingload",TaskUnit:GetGroup()) -local SlingloadMenu=MENU_GROUP:New(TaskGroup,"Slingload cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,SlingloadMenu,self.MenuLoadCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -else -Cargo:Report("Slingload at "..Cargo:GetCoordinate():ToString(TaskUnit:GetGroup())..".","reporting",TaskUnit:GetGroup()) -end -end -end -end -else -Cargo:ReportResetAll(TaskUnit:GetGroup()) -end -end -else -if not Cargo:IsDeployed()==true then -local RouteToPickupMenu=MENU_GROUP:New(TaskGroup,"Route to pickup cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo") -Cargo:ReportResetAll(TaskUnit:GetGroup()) -if Cargo:CanBoard()==true then -if not Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then -local BoardMenu=MENU_GROUP:New(TaskGroup,"Board cargo",RouteToPickupMenu):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,BoardMenu,self.MenuRouteToPickup,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -end -else -if Cargo:CanLoad()==true then -if not Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then -local LoadMenu=MENU_GROUP:New(TaskGroup,"Load cargo",RouteToPickupMenu):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,LoadMenu,self.MenuRouteToPickup,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -end -else -if Cargo:CanSlingload()==true then -if not Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then -local SlingloadMenu=MENU_GROUP:New(TaskGroup,"Slingload cargo",RouteToPickupMenu):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,SlingloadMenu,self.MenuRouteToPickup,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -end -end -end -end -end -end -end -for DeployZoneName,DeployZone in pairs(Task.DeployZones)do -if Cargo:IsInZone(DeployZone)then -Task:I({CargoIsDeployed=Task.CargoDeployed and"true"or"false"}) -if Cargo:IsDeployed()==false then -Cargo:SetDeployed(true) -Task:I({CargoIsAlive=Cargo:IsAlive()and"true"or"false"}) -if Cargo:IsAlive()then -Task:CargoDeployed(TaskUnit,Cargo,DeployZone) -end -end -end -end -end -if Cargo:IsLoaded()==true and Cargo:IsLoadedInCarrier(TaskUnit)==true then -if not TaskUnit:InAir()then -if Cargo:CanUnboard()==true then -local UnboardMenu=MENU_GROUP:New(TaskGroup,"Unboard cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,UnboardMenu,self.MenuUnboardCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -else -if Cargo:CanUnload()==true then -local UnloadMenu=MENU_GROUP:New(TaskGroup,"Unload cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,UnloadMenu,self.MenuUnloadCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -end -end -end -end -for DeployZoneName,DeployZone in pairs(Task.DeployZones)do -if not Cargo:IsInZone(DeployZone)then -local RouteToDeployMenu=MENU_GROUP:New(TaskGroup,"Route to deploy cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo") -MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),"Zone "..DeployZoneName,RouteToDeployMenu,self.MenuRouteToDeploy,self,DeployZone):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() -end -end -end -end -) -Task:RefreshTaskControlMenu(TaskUnit,MenuTime,"Cargo") -self:__SelectAction(-1) -end -function Fsm:OnLeaveWaitingForCommand(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -end -function Fsm:MenuBoardCargo(Cargo) -self:__PrepareBoarding(1.0,Cargo) -end -function Fsm:MenuLoadCargo(Cargo) -self:__Load(1.0,Cargo) -end -function Fsm:MenuUnboardCargo(Cargo,DeployZone) -self:__PrepareUnBoarding(1.0,Cargo,DeployZone) -end -function Fsm:MenuUnloadCargo(Cargo,DeployZone) -self:__Unload(1.0,Cargo,DeployZone) -end -function Fsm:MenuRouteToPickup(Cargo) -self:__RouteToPickup(1.0,Cargo) -end -function Fsm:MenuRouteToDeploy(DeployZone) -self:__RouteToDeploy(1.0,DeployZone) -end -function Fsm:onafterRouteToPickup(TaskUnit,Task,From,Event,To,Cargo) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Cargo:IsAlive()then -self.Cargo=Cargo -Task:SetCargoPickup(self.Cargo,TaskUnit) -self:__RouteToPickupPoint(-0.1) -end -end -function Fsm:onafterArriveAtPickup(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if self.Cargo:IsAlive()then -if TaskUnit:IsAir()then -Task:GetMission():GetCommandCenter():MessageToGroup("Land",TaskUnit:GetGroup()) -self:__Land(-0.1,"Pickup") -else -self:__SelectAction(-0.1) -end -end -end -function Fsm:onafterCancelRouteToPickup(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -Task:GetMission():GetCommandCenter():MessageToGroup("Cancelled routing to Cargo "..self.Cargo:GetName(),TaskUnit:GetGroup()) -self:__SelectAction(-0.1) -end -function Fsm:onafterRouteToDeploy(TaskUnit,Task,From,Event,To,DeployZone) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -self:F(DeployZone) -self.DeployZone=DeployZone -Task:SetDeployZone(self.DeployZone,TaskUnit) -self:__RouteToDeployZone(-0.1) -end -function Fsm:onafterArriveAtDeploy(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if TaskUnit:IsAir()then -Task:GetMission():GetCommandCenter():MessageToGroup("Land",TaskUnit:GetGroup()) -self:__Land(-0.1,"Deploy") -else -self:__SelectAction(-0.1) -end -end -function Fsm:onafterCancelRouteToDeploy(TaskUnit,Task) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -Task:GetMission():GetCommandCenter():MessageToGroup("Cancelled routing to deploy zone "..self.DeployZone:GetName(),TaskUnit:GetGroup()) -self:__SelectAction(-0.1) -end -function Fsm:onafterLand(TaskUnit,Task,From,Event,To,Action) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Action=="Pickup"then -if self.Cargo:IsAlive()then -if self.Cargo:IsInReportRadius(TaskUnit:GetPointVec2())then -if TaskUnit:InAir()then -self:__Land(-10,Action) -else -Task:GetMission():GetCommandCenter():MessageToGroup("Landed at pickup location...",TaskUnit:GetGroup()) -self:__Landed(-0.1,Action) -end -else -self:__RouteToPickup(-0.1,self.Cargo) -end -end -else -if TaskUnit:IsAlive()then -if TaskUnit:IsInZone(self.DeployZone)then -if TaskUnit:InAir()then -self:__Land(-10,Action) -else -Task:GetMission():GetCommandCenter():MessageToGroup("Landed at deploy zone "..self.DeployZone:GetName(),TaskUnit:GetGroup()) -self:__Landed(-0.1,Action) -end -else -self:__RouteToDeploy(-0.1,self.Cargo) -end -end -end -end -function Fsm:onafterLanded(TaskUnit,Task,From,Event,To,Action) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Action=="Pickup"then -if self.Cargo:IsAlive()then -if self.Cargo:IsInReportRadius(TaskUnit:GetPointVec2())then -if TaskUnit:InAir()then -self:__Land(-0.1,Action) -else -self:__SelectAction(-0.1) -end -else -self:__RouteToPickup(-0.1,self.Cargo) -end -end -else -if TaskUnit:IsAlive()then -if TaskUnit:IsInZone(self.DeployZone)then -if TaskUnit:InAir()then -self:__Land(-10,Action) -else -self:__SelectAction(-0.1) -end -else -self:__RouteToDeploy(-0.1,self.Cargo) -end -end -end -end -function Fsm:onafterPrepareBoarding(TaskUnit,Task,From,Event,To,Cargo) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -if Cargo and Cargo:IsAlive()then -self:__Board(-0.1,Cargo) -end -end -function Fsm:onafterBoard(TaskUnit,Task,From,Event,To,Cargo) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) -function Cargo:OnEnterLoaded(From,Event,To,TaskUnit,TaskProcess) -self:F({From,Event,To,TaskUnit,TaskProcess}) -TaskProcess:__Boarded(0.1,self) -end -if Cargo:IsAlive()then -if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then -if TaskUnit:InAir()then -else -Cargo:MessageToGroup("Boarding ...",TaskUnit:GetGroup()) -if not Cargo:IsBoarding()then -Cargo:Board(TaskUnit,nil,self) -end -end -else -end -end -end -function Fsm:onafterBoarded(TaskUnit,Task,From,Event,To,Cargo) -local TaskUnitName=TaskUnit:GetName() -self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()}) -Cargo:MessageToGroup("Boarded cargo "..Cargo:GetName(),TaskUnit:GetGroup()) -self:__Load(-0.1,Cargo) -end -function Fsm:onafterLoad(TaskUnit,Task,From,Event,To,Cargo) -local TaskUnitName=TaskUnit:GetName() -self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()}) -if not Cargo:IsLoaded()then -Cargo:Load(TaskUnit) -end -Cargo:MessageToGroup("Loaded cargo "..Cargo:GetName(),TaskUnit:GetGroup()) -TaskUnit:AddCargo(Cargo) -Task:CargoPickedUp(TaskUnit,Cargo) -self:SelectAction(-1) -end -function Fsm:onafterPrepareUnBoarding(TaskUnit,Task,From,Event,To,Cargo) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID(),From,Event,To,Cargo}) -self.Cargo=Cargo -self.DeployZone=nil -if Cargo:IsAlive()then -for DeployZoneName,DeployZone in pairs(Task.DeployZones)do -if Cargo:IsInZone(DeployZone)then -self.DeployZone=DeployZone -break -end -end -self:__UnBoard(-0.1,Cargo,self.DeployZone) -end -end -function Fsm:onafterUnBoard(TaskUnit,Task,From,Event,To,Cargo,DeployZone) -self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID(),From,Event,To,Cargo,DeployZone}) -function self.Cargo:OnEnterUnLoaded(From,Event,To,DeployZone,TaskProcess) -self:F({From,Event,To,DeployZone,TaskProcess}) -TaskProcess:__UnBoarded(-0.1) -end -if self.Cargo:IsAlive()then -self.Cargo:MessageToGroup("UnBoarding ...",TaskUnit:GetGroup()) -if DeployZone then -self.Cargo:UnBoard(DeployZone:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self) -else -self.Cargo:UnBoard(TaskUnit:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self) -end -end -end -function Fsm:onafterUnBoarded(TaskUnit,Task) -local TaskUnitName=TaskUnit:GetName() -self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()}) -self.Cargo:MessageToGroup("UnBoarded cargo "..self.Cargo:GetName(),TaskUnit:GetGroup()) -self:Unload(self.Cargo) -end -function Fsm:onafterUnload(TaskUnit,Task,From,Event,To,Cargo,DeployZone) -local TaskUnitName=TaskUnit:GetName() -self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()}) -if not Cargo:IsUnLoaded()then -if DeployZone then -Cargo:UnLoad(DeployZone:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self) -else -Cargo:UnLoad(TaskUnit:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self) -end -end -TaskUnit:RemoveCargo(Cargo) -Cargo:MessageToGroup("Unloaded cargo "..Cargo:GetName(),TaskUnit:GetGroup()) -self:Planned() -self:__SelectAction(1) -end -return self -end -function TASK_CARGO:SetCargoLimit(CargoLimit) -self.CargoLimit=CargoLimit -return self -end -function TASK_CARGO:SetSmokeColor(SmokeColor) -if SmokeColor==nil then -self.SmokeColor=SMOKECOLOR.Red -elseif type(SmokeColor)=="number"then -self:F2(SmokeColor) -if SmokeColor>0 and SmokeColor<=5 then -self.SmokeColor=SMOKECOLOR.SmokeColor -end -end -end -function TASK_CARGO:GetSmokeColor() -return self.SmokeColor -end -function TASK_CARGO:GetPlannedMenuText() -return self:GetStateString().." - "..self:GetTaskName().." ( "..self.TargetSetUnit:GetUnitTypesText().." )" -end -function TASK_CARGO:GetCargoSet() -return self.SetCargo -end -function TASK_CARGO:GetDeployZones() -return self.DeployZones -end -function TASK_CARGO:SetCargoPickup(Cargo,TaskUnit) -self:F({Cargo,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local MenuTime=self:InitTaskControlMenu(TaskUnit) -local MenuControl=self:GetTaskControlMenu(TaskUnit) -local ActRouteCargo=ProcessUnit:GetProcess("RoutingToPickup","RouteToPickupPoint") -ActRouteCargo:Reset() -ActRouteCargo:SetCoordinate(Cargo:GetCoordinate()) -ActRouteCargo:SetRange(Cargo:GetLoadRadius()) -ActRouteCargo:SetMenuCancel(TaskUnit:GetGroup(),"Cancel Routing to Cargo "..Cargo:GetName(),MenuControl,MenuTime,"Cargo") -ActRouteCargo:Start() -return self -end -function TASK_CARGO:SetDeployZone(DeployZone,TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local MenuTime=self:InitTaskControlMenu(TaskUnit) -local MenuControl=self:GetTaskControlMenu(TaskUnit) -local ActRouteDeployZone=ProcessUnit:GetProcess("RoutingToDeploy","RouteToDeployZone") -ActRouteDeployZone:Reset() -ActRouteDeployZone:SetZone(DeployZone) -ActRouteDeployZone:SetMenuCancel(TaskUnit:GetGroup(),"Cancel Routing to Deploy Zone"..DeployZone:GetName(),MenuControl,MenuTime,"Cargo") -ActRouteDeployZone:Start() -return self -end -function TASK_CARGO:AddDeployZone(DeployZone,TaskUnit) -self.DeployZones[DeployZone:GetName()]=DeployZone -return self -end -function TASK_CARGO:RemoveDeployZone(DeployZone,TaskUnit) -self.DeployZones[DeployZone:GetName()]=nil -return self -end -function TASK_CARGO:SetDeployZones(DeployZones,TaskUnit) -for DeployZoneID,DeployZone in pairs(DeployZones or{})do -self.DeployZones[DeployZone:GetName()]=DeployZone -end -return self -end -function TASK_CARGO:GetTargetZone(TaskUnit) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone") -return ActRouteTarget:GetZone() -end -function TASK_CARGO:SetScoreOnProgress(Text,Score,TaskUnit) -self:F({Text,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScoreProcess("Engaging","Account","Account",Text,Score) -return self -end -function TASK_CARGO:SetScoreOnSuccess(Text,Score,TaskUnit) -self:F({Text,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Success",Text,Score) -return self -end -function TASK_CARGO:SetScoreOnFail(Text,Penalty,TaskUnit) -self:F({Text,Score,TaskUnit}) -local ProcessUnit=self:GetUnitProcess(TaskUnit) -ProcessUnit:AddScore("Failed",Text,Penalty) -return self -end -function TASK_CARGO:SetGoalTotal() -self.GoalTotal=self.SetCargo:Count() -end -function TASK_CARGO:GetGoalTotal() -return self.GoalTotal -end -function TASK_CARGO:UpdateTaskInfo() -if self:IsStatePlanned()or self:IsStateAssigned()then -self.TaskInfo:AddTaskName(0,"MSOD") -self.TaskInfo:AddCargoSet(self.SetCargo,10,"SOD",true) -local Coordinates={} -for CargoName,Cargo in pairs(self.SetCargo:GetSet())do -local Cargo=Cargo -if not Cargo:IsLoaded()then -Coordinates[#Coordinates+1]=Cargo:GetCoordinate() -end -end -self.TaskInfo:AddCoordinates(Coordinates,1,"M") -end -end -function TASK_CARGO:ReportOrder(ReportGroup) -return 0 -end -function TASK_CARGO:GetAutoAssignPriority(AutoAssignMethod,TaskGroup) -if AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Random then -return math.random(1,9) -elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Distance then -return 0 -elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Priority then -return 1 -end -return 0 -end -end -do -TASK_CARGO_TRANSPORT={ -ClassName="TASK_CARGO_TRANSPORT", -} -function TASK_CARGO_TRANSPORT:New(Mission,SetGroup,TaskName,SetCargo,TaskBriefing) -local self=BASE:Inherit(self,TASK_CARGO:New(Mission,SetGroup,TaskName,SetCargo,"Transport",TaskBriefing)) -self:F() -Mission:AddTask(self) -local Fsm=self:GetUnitProcess() -local CargoReport=REPORT:New("Transport Cargo. The following cargo needs to be transported including initial positions:") -SetCargo:ForEachCargo( -function(Cargo) -local CargoType=Cargo:GetType() -local CargoName=Cargo:GetName() -local CargoCoordinate=Cargo:GetCoordinate() -CargoReport:Add(string.format('- "%s" (%s) at %s',CargoName,CargoType,CargoCoordinate:ToStringMGRS())) -end -) -self:SetBriefing( -TaskBriefing or -CargoReport:Text() -) -return self -end -function TASK_CARGO_TRANSPORT:ReportOrder(ReportGroup) -return 0 -end -function TASK_CARGO_TRANSPORT:IsAllCargoTransported() -local CargoSet=self:GetCargoSet() -local Set=CargoSet:GetSet() -local DeployZones=self:GetDeployZones() -local CargoDeployed=true -for CargoID,CargoData in pairs(Set)do -local Cargo=CargoData -self:F({Cargo=Cargo:GetName(),CargoDeployed=Cargo:IsDeployed()}) -if Cargo:IsDeployed()then -else -CargoDeployed=false -end -end -self:F({CargoDeployed=CargoDeployed}) -return CargoDeployed -end -function TASK_CARGO_TRANSPORT:onafterGoal(TaskUnit,From,Event,To) -local CargoSet=self.CargoSet -if self:IsAllCargoTransported()then -self:Success() -end -self:__Goal(-10) -end -end -do -TASK_CARGO_CSAR={ -ClassName="TASK_CARGO_CSAR", -} -function TASK_CARGO_CSAR:New(Mission,SetGroup,TaskName,SetCargo,TaskBriefing) -local self=BASE:Inherit(self,TASK_CARGO:New(Mission,SetGroup,TaskName,SetCargo,"CSAR",TaskBriefing)) -self:F() -Mission:AddTask(self) -self:AddTransition("*","CargoPickedUp","*") -self:AddTransition("*","CargoDeployed","*") -self:F({CargoDeployed=self.CargoDeployed~=nil and"true"or"false"}) -local Fsm=self:GetUnitProcess() -local CargoReport=REPORT:New("Rescue a downed pilot from the following position:") -SetCargo:ForEachCargo( -function(Cargo) -local CargoType=Cargo:GetType() -local CargoName=Cargo:GetName() -local CargoCoordinate=Cargo:GetCoordinate() -CargoReport:Add(string.format('- "%s" (%s) at %s',CargoName,CargoType,CargoCoordinate:ToStringMGRS())) -end -) -self:SetBriefing( -TaskBriefing or -CargoReport:Text() -) -return self -end -function TASK_CARGO_CSAR:ReportOrder(ReportGroup) -return 0 -end -function TASK_CARGO_CSAR:IsAllCargoTransported() -local CargoSet=self:GetCargoSet() -local Set=CargoSet:GetSet() -local DeployZones=self:GetDeployZones() -local CargoDeployed=true -for CargoID,CargoData in pairs(Set)do -local Cargo=CargoData -self:F({Cargo=Cargo:GetName(),CargoDeployed=Cargo:IsDeployed()}) -if Cargo:IsDeployed()then -else -CargoDeployed=false -end -end -self:F({CargoDeployed=CargoDeployed}) -return CargoDeployed -end -function TASK_CARGO_CSAR:onafterGoal(TaskUnit,From,Event,To) -local CargoSet=self.CargoSet -if self:IsAllCargoTransported()then -self:Success() -end -self:__Goal(-10) -end -end -do -TASK_CARGO_DISPATCHER={ -ClassName="TASK_CARGO_DISPATCHER", -Mission=nil, -Tasks={}, -CSAR={}, -CSARSpawned=0, -Transport={}, -TransportCount=0, -} -function TASK_CARGO_DISPATCHER:New(Mission,SetGroup) -local self=BASE:Inherit(self,TASK_MANAGER:New(SetGroup)) -self.Mission=Mission -self:AddTransition("Started","Assign","Started") -self:AddTransition("Started","CargoPickedUp","Started") -self:AddTransition("Started","CargoDeployed","Started") -self:SetCSARRadius() -self:__StartTasks(5) -self.MaxCSAR=nil -self.CountCSAR=0 -self:HandleEvent(EVENTS.Ejection) -return self -end -function TASK_CARGO_DISPATCHER:SetCSARZones(SetZonesCSAR) -self.SetZonesCSAR=SetZonesCSAR -end -function TASK_CARGO_DISPATCHER:SetMaxCSAR(MaxCSAR) -self.MaxCSAR=MaxCSAR -end -function TASK_CARGO_DISPATCHER:OnEventEjection(EventData) -self:F({EventData=EventData}) -if self.CSARTasks==true then -local CSARCoordinate=EventData.IniUnit:GetCoordinate() -local CSARCoalition=EventData.IniUnit:GetCoalition() -local CSARCountry=EventData.IniUnit:GetCountry() -local CSARHeading=EventData.IniUnit:GetHeading() -if CSARCoalition==self.Mission:GetCommandCenter():GetCoalition()then -if not self.SetZonesCSAR or(self.SetZonesCSAR and self.SetZonesCSAR:IsCoordinateInZone(CSARCoordinate))then -if not self.MaxCSAR or(self.MaxCSAR and self.CountCSAR