From b84d08f052f3205ca26bcf86cf7f884605d564e6 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 29 Sep 2017 07:03:50 +0200 Subject: [PATCH] Progress --- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 118 +- .../Moose/Functional/Protect.lua | 85 +- Moose Mission Setup/Moose.lua | 26964 +++++++++++++++- 4 files changed, 27068 insertions(+), 101 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 5eae5b712..8fefad5d5 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2502,7 +2502,7 @@ do -- SET_STATIC function SET_STATIC:AddInDatabase( Event ) self:F3( { Event } ) - if Event.IniObjectCategory == 1 then + if Event.IniObjectCategory == Object.Category.STATIC then if not self.Database[Event.IniDCSStaticName] then self.Database[Event.IniDCSStaticName] = STATIC:Register( Event.IniDCSStaticName ) self:T3( self.Database[Event.IniDCSStaticName] ) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index b1c672c10..f52988f6f 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -562,6 +562,118 @@ function ZONE_RADIUS:GetVec3( Height ) end +--- Scan the zone +-- @param #ZONE_RADIUS self +-- @param Coalition +function ZONE_RADIUS:Scan() + + self.Coalitions = {} + + local ZoneCoord = self:GetCoordinate() + local ZoneRadius = self:GetRadius() + + self:E({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()}) + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = ZoneCoord:GetVec3(), + radius = ZoneRadius, + } + } + + local function EvaluateZone( ZoneDCSUnit ) + if ZoneDCSUnit:isExist() then + local CategoryDCSUnit = ZoneDCSUnit:getCategory() + if ( CategoryDCSUnit == Object.Category.UNIT and ZoneDCSUnit:isActive() ) or + CategoryDCSUnit == Object.Category.STATIC then + local CoalitionDCSUnit = ZoneDCSUnit:getCoalition() + self.Coalitions[CoalitionDCSUnit] = true + self:E( { Name = ZoneDCSUnit:getName(), Coalition = CoalitionDCSUnit } ) + end + end + return true + end + + world.searchObjects( { Object.Category.UNIT, Object.Category.STATIC }, SphereSearch, EvaluateZone ) + +end + + +function ZONE_RADIUS:CountCoalitions() + + local Count = 0 + + for CoalitionID, Coalition in pairs( self.Coalitions ) do + Count = Count + 1 + end + return Count +end + +--- Is All in Zone of Coalition? +-- @param #ZONE_RADIUS self +-- @param Coalition +-- @return #boolean +function ZONE_RADIUS:IsAllInZoneOfCoalition( Coalition ) + + return self:CountCoalitions() == 1 and self.Coalitions[Coalition] == true +end + + +--- Is All in Zone of Other Coalition? +-- @param #ZONE_RADIUS self +-- @param Coalition +-- @return #boolean +function ZONE_RADIUS:IsAllInZoneOfOtherCoalition( Coalition ) + + self:E( { Coalitions = self.Coalitions, Count = self:CountCoalitions() } ) + return self:CountCoalitions() == 1 and self.Coalitions[Coalition] == nil +end + + +--- Is Some in Zone of Coalition? +-- @param #ZONE_RADIUS self +-- @param Coalition +-- @return #boolean +function ZONE_RADIUS:IsSomeInZoneOfCoalition( Coalition ) + + return self:CountCoalitions() > 1 and self.Coalitions[Coalition] == true +end + + +--- Is None in Zone of Coalition? +-- @param #ZONE_RADIUS self +-- @param Coalition +-- @return #boolean +function ZONE_RADIUS:IsNoneInZoneOfCoalition( Coalition ) + + return self.Coalitions[Coalition] == nil +end + + +--- Get the Zone Coalitions. +-- Returns nil if there are none ot two coalitions in the zone! +-- @param #ZONE_RADIUS self +-- @return Coalitions +function ZONE_RADIUS:GetCoalition() + + local Count = 0 + local ReturnCoalition = nil + + for CoalitionID, Coalition in pairs( self.Coalitions ) do + Count = Count + 1 + ReturnCoalition = CoalitionID + end + + if Count ~= 1 then + ReturnCoalition = nil + end + + return ReturnCoalition +end + + + --- Searches the zone -- @param #ZONE_RADIUS self -- @param EvaluateFunction @@ -582,11 +694,11 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction ) } } - local function EvaluateZone( DCSZoneUnit ) + local function EvaluateZone( ZoneDCSUnit ) - env.info( DCSZoneUnit:getName() ) + env.info( ZoneDCSUnit:getName() ) - local ZoneUnit = UNIT:Find( DCSZoneUnit ) + local ZoneUnit = UNIT:Find( ZoneDCSUnit ) return EvaluateFunction( ZoneUnit ) end diff --git a/Moose Development/Moose/Functional/Protect.lua b/Moose Development/Moose/Functional/Protect.lua index a601a3004..d462ac37e 100644 --- a/Moose Development/Moose/Functional/Protect.lua +++ b/Moose Development/Moose/Functional/Protect.lua @@ -25,12 +25,13 @@ PROTECT = { --- PROTECT constructor. -- @param #PROTECT self -- @param Core.Zone#ZONE ProtectZone A @{Zone} object to protect. +-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. -- @return #PROTECT -- @usage -- -- Protect the zone -- ProtectZone = PROTECT:New( ZONE:New( "Zone" ) ) -- -function PROTECT:New( ProtectZone ) +function PROTECT:New( ProtectZone, Coalition ) local self = BASE:Inherit( self, FSM:New() ) -- #PROTECT @@ -39,22 +40,41 @@ function PROTECT:New( ProtectZone ) self.ProtectStaticSet = SET_STATIC:New() self.CaptureUnitSet = SET_UNIT:New() - self:SetStartState( "Idle" ) + self:SetStartState( "-" ) - self:AddTransition( { "Idle", "Captured" }, "Protect", "Protecting" ) + self:AddTransition( { "-", "Protected", "Captured" }, "Protected", "Protected" ) - self:AddTransition( "Protecting", "Check", "Protecting" ) + self:AddTransition( { "Protected", "Attacked" }, "Destroyed", "Destroyed" ) - self:AddTransition( "Protecting", "Capture", "Captured" ) + self:AddTransition( { "Protected", "Destroyed" }, "Attacked", "Attacked" ) + + self:AddTransition( { "Protected", "Attacked", "Destroyed" }, "Captured", "Captured" ) - self:AddTransition( { "Protecting", "Captured" }, "Leave", "Idle" ) + self:ScheduleRepeat( 60, 60, 0, nil, self.Status, self ) - --self:ScheduleRepeat( 1, 5, 0, nil, self.CheckScheduler, self ) + self:SetCoalition( Coalition ) + + self:__Protected( 5 ) return self end +--- Set the owning coalition of the zone. +-- @param #PROTECT self +-- @param DCSCoalition.DCSCoalition#coalition Coalition +function PROTECT:SetCoalition( Coalition ) + self.Coalition = Coalition +end + + +--- Get the owning coalition of the zone. +-- @param #PROTECT self +-- @return DCSCoalition.DCSCoalition#coalition Coalition +function PROTECT:GetCoalition() + return self.Coalition +end + --- Add a unit to the protection. -- @param #PROTECT self @@ -99,6 +119,30 @@ function PROTECT:GetCaptureUnitSet() end +function PROTECT:IsProtected() + + local IsAllCoalition = self.ProtectZone:IsAllInZoneOfCoalition( self.Coalition ) + self:E( { IsAllCoalition = IsAllCoalition } ) + return IsAllCoalition +end + +function PROTECT:IsCaptured() + + local IsCaptured = self.ProtectZone:IsAllInZoneOfOtherCoalition( self.Coalition ) + self:E( { IsCaptured = IsCaptured } ) + return IsCaptured +end + + +function PROTECT:IsAttacked() + + local IsSomeCoalition = self.ProtectZone:IsSomeInZoneOfCoalition( self.Coalition ) + self:E( { IsSomeCoalition = IsSomeCoalition } ) + return IsSomeCoalition +end + + + --- Check if the units are still alive. -- @param #PROTECT self function PROTECT:AreProtectUnitsAlive() @@ -164,21 +208,30 @@ function PROTECT:Smoke( SmokeColor ) end -function PROTECT:onafterProtect() +function PROTECT:onenterCaptured() - self:Check() + local NewCoalition = self.ProtectZone:GetCoalition() + self:E( { NewCoalition = NewCoalition } ) + self:SetCoalition( NewCoalition ) end -function PROTECT:onafterCheck() +--- Check status ProtectZone. +-- @param #PROTECT self +function PROTECT:Status() - if ( self.ProtectUnitSet and self:AreProtectUnitsAlive() ) or - ( self.ProtectStaticSet and self:AreProtectStaticsAlive() ) or - self:IsCaptureUnitInZone() == false then - self:__Check( -1 ) + self.ProtectZone:Scan() + + if self:IsProtected() then + self:Protected() else - self:Capture() + if self:IsAttacked() then + self:Attacked() + else + if self:IsCaptured() then + self:Captured() + end + end end - end diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 640d3276c..921a49671 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,86 +1,26888 @@ -env.info('*** MOOSE DYNAMIC INCLUDE START *** ') -env.info('Moose Generation Timestamp: 20170928_1657') -local base=_G -__Moose={} -__Moose.Include=function(IncludeFile) -if not __Moose.Includes[IncludeFile]then -__Moose.Includes[IncludeFile]=IncludeFile -local f=assert(base.loadfile(__Moose.ProgramPath..IncludeFile)) -if f==nil then -error("Moose: Could not load Moose file "..IncludeFile) +env.info('*** MOOSE STATIC INCLUDE START *** ') +env.info('Moose Generation Timestamp: 20170929_0658') +env.setErrorMessageBoxEnabled(false) +routines={} +routines.majorVersion=3 +routines.minorVersion=3 +routines.build=22 +routines.utils={} +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 -env.info("Moose: "..IncludeFile.." dynamically loaded from "..__Moose.ProgramPath) -return f() +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 +return tostring(tbl) +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('%q',s) +return s end end end -__Moose.ProgramPath="Scripts/Moose/" -__Moose.Includes={} -__Moose.Include('Utilities/Routines.lua') -__Moose.Include('Utilities/Utils.lua') -__Moose.Include('Core/Base.lua') -__Moose.Include('Core/Report.lua') -__Moose.Include('Core/Scheduler.lua') -__Moose.Include('Core/ScheduleDispatcher.lua') -__Moose.Include('Core/Event.lua') -__Moose.Include('Core/Settings.lua') -__Moose.Include('Core/Menu.lua') -__Moose.Include('Core/Zone.lua') -__Moose.Include('Core/Database.lua') -__Moose.Include('Core/Set.lua') -__Moose.Include('Core/Point.lua') -__Moose.Include('Core/Message.lua') -__Moose.Include('Core/Fsm.lua') -__Moose.Include('Core/Radio.lua') -__Moose.Include('Core/SpawnStatic.lua') -__Moose.Include('Core/Cargo.lua') -__Moose.Include('Core/Spot.lua') -__Moose.Include('Wrapper/Object.lua') -__Moose.Include('Wrapper/Identifiable.lua') -__Moose.Include('Wrapper/Positionable.lua') -__Moose.Include('Wrapper/Controllable.lua') -__Moose.Include('Wrapper/Group.lua') -__Moose.Include('Wrapper/Unit.lua') -__Moose.Include('Wrapper/Client.lua') -__Moose.Include('Wrapper/Static.lua') -__Moose.Include('Wrapper/Airbase.lua') -__Moose.Include('Wrapper/Scenery.lua') -__Moose.Include('Functional/Scoring.lua') -__Moose.Include('Functional/CleanUp.lua') -__Moose.Include('Functional/Spawn.lua') -__Moose.Include('Functional/Movement.lua') -__Moose.Include('Functional/Sead.lua') -__Moose.Include('Functional/Escort.lua') -__Moose.Include('Functional/MissileTrainer.lua') -__Moose.Include('Functional/AirbasePolice.lua') -__Moose.Include('Functional/Detection.lua') -__Moose.Include('Functional/Designate.lua') -__Moose.Include('Functional/RAT.lua') -__Moose.Include('Functional/Protect.lua') -__Moose.Include('AI/AI_Balancer.lua') -__Moose.Include('AI/AI_A2A.lua') -__Moose.Include('AI/AI_A2A_Patrol.lua') -__Moose.Include('AI/AI_A2A_Cap.lua') -__Moose.Include('AI/AI_A2A_Gci.lua') -__Moose.Include('AI/AI_A2A_Dispatcher.lua') -__Moose.Include('AI/AI_Patrol.lua') -__Moose.Include('AI/AI_Cap.lua') -__Moose.Include('AI/AI_Cas.lua') -__Moose.Include('AI/AI_Bai.lua') -__Moose.Include('AI/AI_Formation.lua') -__Moose.Include('Actions/Act_Assign.lua') -__Moose.Include('Actions/Act_Route.lua') -__Moose.Include('Actions/Act_Account.lua') -__Moose.Include('Actions/Act_Assist.lua') -__Moose.Include('Tasking/CommandCenter.lua') -__Moose.Include('Tasking/Mission.lua') -__Moose.Include('Tasking/Task.lua') -__Moose.Include('Tasking/DetectionManager.lua') -__Moose.Include('Tasking/Task_A2G_Dispatcher.lua') -__Moose.Include('Tasking/Task_A2G.lua') -__Moose.Include('Tasking/Task_A2A_Dispatcher.lua') -__Moose.Include('Tasking/Task_A2A.lua') -__Moose.Include('Tasking/Task_Cargo.lua') -__Moose.Include('Moose.lua') -BASE:TraceOnOff(true) +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 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 +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) +self:E(err_str) +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.MetersToFeet=function(meters) +return meters/0.3048 +end +UTILS.NMToMeters=function(NM) +return NM*1852 +end +UTILS.FeetToMeters=function(feet) +return feet*0.3048 +end +UTILS.MpsToKnots=function(mps) +return mps*3600/1852 +end +UTILS.MpsToKmph=function(mps) +return mps*3.6 +end +UTILS.KnotsToMps=function(knots) +return knots*1852/3600 +end +UTILS.KnotsToKmph=function(knots) +return knots*1.852 +end +UTILS.KmphToMps=function(kmph) +return kmph/3.6 +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' +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=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('%02d',latDeg)..' '..string.format(minFrmtStr,latMin)..'\''..latHemi..' ' +..string.format('%02d',lonDeg)..' '..string.format(minFrmtStr,lonMin)..'\''..lonHemi +end +end +UTILS.tostringMGRS=function(MGRS,acc) +if acc==0 then +return MGRS.UTMZone..' '..MGRS.MGRSDigraph +else +return MGRS.UTMZone..' '..MGRS.MGRSDigraph..' '..string.format('%0'..acc..'d',UTILS.Round(MGRS.Easting/(10^(5-acc)),0)) +..' '..string.format('%0'..acc..'d',UTILS.Round(MGRS.Northing/(10^(5-acc)),0)) +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.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 +local _TraceOnOff=true +local _TraceLevel=1 +local _TraceAll=false +local _TraceClass={} +local _TraceClassMethod={} +local _ClassID=0 +BASE={ +ClassName="BASE", +ClassID=0, +Events={}, +States={}, +} +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 +function BASE:GetParent(Child) +local Parent +if Child.ClassName=='BASE'then +Parent=nil +elseif rawget(Child,"__")then +Parent=getmetatable(Child.__).__index +else +Parent=getmetatable(Child).__index +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=self:GetParent(self) +while Parent do +if string.upper(Parent.ClassName)==ClassName then +return true +end +Parent=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(Event,EventFunction) +self:EventDispatcher():OnEventGeneric(EventFunction,self,Event) +return self +end +function BASE:UnHandleEvent(Event) +self:EventDispatcher():RemoveEvent(self,Event) +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: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: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}) +self.SchedulerObject=self +local ScheduleID=_SCHEDULEDISPATCHER:AddSchedule( +self, +SchedulerFunction, +{...}, +Start, +nil, +nil, +nil +) +self._.Schedules[#self.Schedules+1]=ScheduleID +return 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}) +self.SchedulerObject=self +local ScheduleID=_SCHEDULEDISPATCHER:AddSchedule( +self, +SchedulerFunction, +{...}, +Start, +Repeat, +RandomizeFactor, +Stop +) +self._.Schedules[SchedulerFunction]=ScheduleID +return self._.Schedules +end +function BASE:ScheduleStop(SchedulerFunction) +self:F3({"ScheduleStop:"}) +_SCHEDULEDISPATCHER:Stop(self,self._.Schedules[SchedulerFunction]) +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:TraceOnOff(TraceOnOff) +_TraceOnOff=TraceOnOff +end +function BASE:IsTrace() +if 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 +self:E("Tracing level "..Level) +end +function BASE:TraceAll(TraceAll) +_TraceAll=TraceAll +if _TraceAll then +self:E("Tracing all methods in MOOSE ") +else +self:E("Switched off tracing all methods in MOOSE") +end +end +function BASE:TraceClass(Class) +_TraceClass[Class]=true +_TraceClassMethod[Class]={} +self:E("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:E("Tracing method "..Method.." of class "..Class) +end +function BASE:_F(Arguments,DebugInfoCurrentParam,DebugInfoFromParam) +if debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then +local DebugInfoCurrent=DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo(2,"nl") +local DebugInfoFrom=DebugInfoFromParam and DebugInfoFromParam or 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:%20s%05d.%s(%s)",LineCurrent,LineFrom,"F",self.ClassName,self.ClassID,Function,routines.utils.oneLineSerialize(Arguments))) +end +end +end +function BASE:F(Arguments) +if debug and _TraceOnOff then +local DebugInfoCurrent=debug.getinfo(2,"nl") +local DebugInfoFrom=debug.getinfo(3,"l") +if _TraceLevel>=1 then +self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom) +end +end +end +function BASE:F2(Arguments) +if debug and _TraceOnOff then +local DebugInfoCurrent=debug.getinfo(2,"nl") +local DebugInfoFrom=debug.getinfo(3,"l") +if _TraceLevel>=2 then +self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom) +end +end +end +function BASE:F3(Arguments) +if debug and _TraceOnOff then +local DebugInfoCurrent=debug.getinfo(2,"nl") +local DebugInfoFrom=debug.getinfo(3,"l") +if _TraceLevel>=3 then +self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom) +end +end +end +function BASE:_T(Arguments,DebugInfoCurrentParam,DebugInfoFromParam) +if debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then +local DebugInfoCurrent=DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo(2,"nl") +local DebugInfoFrom=DebugInfoFromParam and DebugInfoFromParam or 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:%20s%05d.%s",LineCurrent,LineFrom,"T",self.ClassName,self.ClassID,routines.utils.oneLineSerialize(Arguments))) +end +end +end +function BASE:T(Arguments) +if debug and _TraceOnOff then +local DebugInfoCurrent=debug.getinfo(2,"nl") +local DebugInfoFrom=debug.getinfo(3,"l") +if _TraceLevel>=1 then +self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom) +end +end +end +function BASE:T2(Arguments) +if debug and _TraceOnOff then +local DebugInfoCurrent=debug.getinfo(2,"nl") +local DebugInfoFrom=debug.getinfo(3,"l") +if _TraceLevel>=2 then +self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom) +end +end +end +function BASE:T3(Arguments) +if debug and _TraceOnOff then +local DebugInfoCurrent=debug.getinfo(2,"nl") +local DebugInfoFrom=debug.getinfo(3,"l") +if _TraceLevel>=3 then +self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom) +end +end +end +function BASE:E(Arguments) +if debug then +local DebugInfoCurrent=debug.getinfo(2,"nl") +local DebugInfoFrom=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:%20s%05d.%s(%s)",LineCurrent,LineFrom,"E",self.ClassName,self.ClassID,Function,routines.utils.oneLineSerialize(Arguments))) +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) +self.Report[#self.Report+1]=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={}, +} +function SCHEDULER:New(SchedulerObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop) +local self=BASE:Inherit(self,BASE:New()) +self:F2({Start,Repeat,RandomizeFactor,Stop}) +local ScheduleID=nil +self.MasterObject=SchedulerObject +if SchedulerFunction then +ScheduleID=self:Schedule(SchedulerObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop) +end +return self,ScheduleID +end +function SCHEDULER:Schedule(SchedulerObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop) +self:F2({Start,Repeat,RandomizeFactor,Stop}) +self:T3({SchedulerArguments}) +local ObjectName="-" +if SchedulerObject and SchedulerObject.ClassName and SchedulerObject.ClassID then +ObjectName=SchedulerObject.ClassName..SchedulerObject.ClassID +end +self:F3({"Schedule :",ObjectName,tostring(SchedulerObject),Start,Repeat,RandomizeFactor,Stop}) +self.SchedulerObject=SchedulerObject +local ScheduleID=_SCHEDULEDISPATCHER:AddSchedule( +self, +SchedulerFunction, +SchedulerArguments, +Start, +Repeat, +RandomizeFactor, +Stop +) +self.Schedules[#self.Schedules+1]=ScheduleID +return ScheduleID +end +function SCHEDULER:Start(ScheduleID) +self:F3({ScheduleID}) +_SCHEDULEDISPATCHER:Start(self,ScheduleID) +end +function SCHEDULER:Stop(ScheduleID) +self:F3({ScheduleID}) +_SCHEDULEDISPATCHER:Stop(self,ScheduleID) +end +function SCHEDULER:Remove(ScheduleID) +self:F3({ScheduleID}) +_SCHEDULEDISPATCHER:Remove(self,ScheduleID) +end +function SCHEDULER:Clear() +self:F3() +_SCHEDULEDISPATCHER:Clear(self) +end +SCHEDULEDISPATCHER={ +ClassName="SCHEDULEDISPATCHER", +CallID=0, +} +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) +self:F2({Scheduler,ScheduleFunction,ScheduleArguments,Start,Repeat,Randomize,Stop}) +self.CallID=self.CallID+1 +local CallID=self.CallID.."#"..(Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID()or"")or"" +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+.1 +self.Schedule[Scheduler][CallID].Repeat=Repeat or 0 +self.Schedule[Scheduler][CallID].Randomize=Randomize or 0 +self.Schedule[Scheduler][CallID].Stop=Stop +self:T3(self.Schedule[Scheduler][CallID]) +self.Schedule[Scheduler][CallID].CallHandler=function(CallID) +self:F2(CallID) +local ErrorHandler=function(errmsg) +env.info("Error in timer function: "..errmsg) +if debug~=nil then +env.info(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 ScheduleObject=Scheduler.SchedulerObject +local ScheduleFunction=Schedule.Function +local ScheduleArguments=Schedule.Arguments +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 Status,Result +if ScheduleObject then +local function Timer() +return ScheduleFunction(ScheduleObject,unpack(ScheduleArguments)) +end +Status,Result=xpcall(Timer,ErrorHandler) +else +local function Timer() +return ScheduleFunction(unpack(ScheduleArguments)) +end +Status,Result=xpcall(Timer,ErrorHandler) +end +local CurrentTime=timer.getTime() +local StartTime=Schedule.StartTime +self:F3({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.01 +return ScheduleTime +else +self:Stop(Scheduler,CallID) +end +else +self:Stop(Scheduler,CallID) +end +else +self:E("Scheduled obsolete call for CallID: "..CallID) +end +return nil +end +self:Start(Scheduler,CallID) +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) +self:F2({Start=CallID,Scheduler=Scheduler}) +if CallID then +local Schedule=self.Schedule[Scheduler] +if not Schedule[CallID].ScheduleID then +Schedule[CallID].StartTime=timer.getTime() +Schedule[CallID].ScheduleID=timer.scheduleFunction( +Schedule[CallID].CallHandler, +CallID, +timer.getTime()+Schedule[CallID].Start +) +end +else +for CallID,Schedule in pairs(self.Schedule[Scheduler]or{})do +self:Start(Scheduler,CallID) +end +end +end +function SCHEDULEDISPATCHER:Stop(Scheduler,CallID) +self:F2({Stop=CallID,Scheduler=Scheduler}) +if CallID then +local Schedule=self.Schedule[Scheduler] +if Schedule[CallID].ScheduleID then +timer.removeFunction(Schedule[CallID].ScheduleID) +Schedule[CallID].ScheduleID=nil +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 +EVENT={ +ClassName="EVENT", +ClassID=0, +} +world.event.S_EVENT_NEW_CARGO=world.event.S_EVENT_MAX+1000 +world.event.S_EVENT_DELETE_CARGO=world.event.S_EVENT_MAX+1001 +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, +NewCargo=world.event.S_EVENT_NEW_CARGO, +DeleteCargo=world.event.S_EVENT_DELETE_CARGO, +} +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" +}, +[EVENTS.NewCargo]={ +Order=1, +Event="OnEventNewCargo", +Text="S_EVENT_NEW_CARGO" +}, +[EVENTS.DeleteCargo]={ +Order=1, +Event="OnEventDeleteCargo", +Text="S_EVENT_DELETE_CARGO" +}, +} +function EVENT:New() +local self=BASE:Inherit(self,BASE:New()) +self:F2() +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]=self.Events[EventID][EventPriority][EventClass] +self.Events[EventID][EventPriority][EventClass]=nil +end +function EVENT:Reset(EventObject) +self:E({"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(EventObject) +self:F3({EventObject:GetClassNameAndID()}) +local EventClass=EventObject:GetClassNameAndID() +local EventPriority=EventClass:GetEventPriority() +for EventID,EventData in pairs(self.Events)do +self.Events[EventID][EventPriority][EventClass]=nil +end +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}) +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,...) +self:E(GroupName) +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:CreateEventPlayerEnterUnit(PlayerUnit) +self:F({PlayerUnit}) +local Event={ +id=EVENTS.PlayerEnterUnit, +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 debug~=nil then +env.info(debug.traceback()) +end +return errmsg +end +local EventMeta=_EVENTMETA[Event.id] +if self and +self.Events and +self.Events[Event.id]and +(Event.initiator~=nil or(Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit))then +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) +if Event.IniGroup then +Event.IniGroupName=Event.IniDCSGroupName +end +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 +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 +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 +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) +if Event.TgtGroup then +Event.TgtGroupName=Event.TgtDCSGroupName +end +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 +Event.TgtDCSUnit=Event.target +Event.TgtDCSUnitName=Event.TgtDCSUnit:getName() +Event.TgtUnitName=Event.TgtDCSUnitName +Event.TgtUnit=STATIC:FindByName(Event.TgtDCSUnitName) +Event.TgtCoalition=Event.TgtDCSUnit:getCoalition() +Event.TgtCategory=Event.TgtDCSUnit:getDesc().category +Event.TgtTypeName=Event.TgtDCSUnit:getTypeName() +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.cargo then +Event.Cargo=Event.cargo +Event.CargoName=Event.cargo.Name +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:E({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.Crash or +Event.id==EVENTS.Dead 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:E({"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:E({"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.Crash or +Event.id==EVENTS.Dead 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:E({"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:E({"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 +else +self:E({EventMeta.Text,Event}) +end +Event=nil +end +EVENTHANDLER={ +ClassName="EVENTHANDLER", +ClassID=0, +} +function EVENTHANDLER:New() +self=BASE:Inherit(self,BASE:New()) +return self +end +SETTINGS={ +ClassName="SETTINGS", +} +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) +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: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() +self:E({BRA=(self.A2ASystem and self.A2ASystem=="BRAA")or(not self.A2ASystem and _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 A2GCoordinateMenu=MENU_GROUP:New(MenuGroup,"A2G Coordinate System",SettingsMenu):SetTime(MenuTime) +if not self:IsA2G_LL_DMS()then +MENU_GROUP_COMMAND:New(MenuGroup,"Lat/Lon Degree Min Sec (LL DMS)",A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"LL DMS"):SetTime(MenuTime) +end +if not self:IsA2G_LL_DDM()then +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 +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 +MENU_GROUP_COMMAND:New(MenuGroup,"Bearing, Range (BR)",A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"BR"):SetTime(MenuTime) +end +if not self:IsA2G_MGRS()then +MENU_GROUP_COMMAND:New(MenuGroup,"Military Grid (MGRS)",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 A2ACoordinateMenu=MENU_GROUP:New(MenuGroup,"A2A Coordinate System",SettingsMenu):SetTime(MenuTime) +if not self:IsA2A_LL_DMS()then +MENU_GROUP_COMMAND:New(MenuGroup,"Lat/Lon Degree Min Sec (LL DMS)",A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"LL DMS"):SetTime(MenuTime) +end +if not self:IsA2A_LL_DDM()then +MENU_GROUP_COMMAND:New(MenuGroup,"Lat/Lon Degree Dec Min (LL DDM)",A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"LL DDM"):SetTime(MenuTime) +end +if self:IsA2A_LL_DDM()then +MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 1",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime) +MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 2",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime) +MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 3",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime) +end +if not self:IsA2A_BULLS()then +MENU_GROUP_COMMAND:New(MenuGroup,"Bullseye (BULLS)",A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"BULLS"):SetTime(MenuTime) +end +if not self:IsA2A_BRAA()then +MENU_GROUP_COMMAND:New(MenuGroup,"Bearing Range Altitude Aspect (BRAA)",A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"BRAA"):SetTime(MenuTime) +end +if not self:IsA2A_MGRS()then +MENU_GROUP_COMMAND:New(MenuGroup,"Military Grid (MGRS)",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 MetricsMenu=MENU_GROUP:New(MenuGroup,"Measures and Weights System",SettingsMenu):SetTime(MenuTime) +if self:IsMetric()then +MENU_GROUP_COMMAND:New(MenuGroup,"Imperial (Miles,Feet)",MetricsMenu,self.MenuMWSystem,self,MenuGroup,RootMenu,false):SetTime(MenuTime) +end +if self:IsImperial()then +MENU_GROUP_COMMAND:New(MenuGroup,"Metric (Kilometers,Meters)",MetricsMenu,self.MenuMWSystem,self,MenuGroup,RootMenu,true):SetTime(MenuTime) +end +local MessagesMenu=MENU_GROUP:New(MenuGroup,"Messages and Reports",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:SetPlayerMenu(PlayerUnit) +local PlayerGroup=PlayerUnit:GetGroup() +local PlayerName=PlayerUnit:GetPlayerName() +local PlayerNames=PlayerGroup:GetPlayerNames() +local PlayerMenu=MENU_GROUP:New(PlayerGroup,'Settings "'..PlayerName..'"') +self.PlayerMenu=PlayerMenu +local A2GCoordinateMenu=MENU_GROUP:New(PlayerGroup,"A2G Coordinate System",PlayerMenu) +if not self:IsA2G_LL_DMS()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Lat/Lon Degree Min Sec (LL DMS)",A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DMS") +end +if not self:IsA2G_LL_DDM()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Lat/Lon Degree Dec Min (LL DDM)",A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DDM") +end +if self:IsA2G_LL_DDM()then +MENU_GROUP_COMMAND:New(PlayerGroup,"LL DDM Accuracy 1",A2GCoordinateMenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1) +MENU_GROUP_COMMAND:New(PlayerGroup,"LL DDM Accuracy 2",A2GCoordinateMenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2) +MENU_GROUP_COMMAND:New(PlayerGroup,"LL DDM Accuracy 3",A2GCoordinateMenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3) +end +if not self:IsA2G_BR()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Bearing, Range (BR)",A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"BR") +end +if not self:IsA2G_MGRS()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Military Grid (MGRS)",A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"MGRS") +end +if self:IsA2G_MGRS()then +MENU_GROUP_COMMAND:New(PlayerGroup,"MGRS Accuracy 1",A2GCoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1) +MENU_GROUP_COMMAND:New(PlayerGroup,"MGRS Accuracy 2",A2GCoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2) +MENU_GROUP_COMMAND:New(PlayerGroup,"MGRS Accuracy 3",A2GCoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3) +MENU_GROUP_COMMAND:New(PlayerGroup,"MGRS Accuracy 4",A2GCoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,4) +MENU_GROUP_COMMAND:New(PlayerGroup,"MGRS Accuracy 5",A2GCoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,5) +end +local A2ACoordinateMenu=MENU_GROUP:New(PlayerGroup,"A2A Coordinate System",PlayerMenu) +if not self:IsA2A_LL_DMS()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Lat/Lon Degree Min Sec (LL DMS)",A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DMS") +end +if not self:IsA2A_LL_DDM()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Lat/Lon Degree Dec Min (LL DDM)",A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DDM") +end +if self:IsA2A_LL_DDM()then +MENU_GROUP_COMMAND:New(PlayerGroup,"LL DDM Accuracy 1",A2GCoordinateMenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1) +MENU_GROUP_COMMAND:New(PlayerGroup,"LL DDM Accuracy 2",A2GCoordinateMenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2) +MENU_GROUP_COMMAND:New(PlayerGroup,"LL DDM Accuracy 3",A2GCoordinateMenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3) +end +if not self:IsA2A_BULLS()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Bullseye (BULLS)",A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"BULLS") +end +if not self:IsA2A_BRAA()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Bearing Range Altitude Aspect (BRAA)",A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"BRAA") +end +if not self:IsA2A_MGRS()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Military Grid (MGRS)",A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"MGRS") +end +if self:IsA2A_MGRS()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Military Grid (MGRS) Accuracy 1",A2ACoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1) +MENU_GROUP_COMMAND:New(PlayerGroup,"Military Grid (MGRS) Accuracy 2",A2ACoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2) +MENU_GROUP_COMMAND:New(PlayerGroup,"Military Grid (MGRS) Accuracy 3",A2ACoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3) +MENU_GROUP_COMMAND:New(PlayerGroup,"Military Grid (MGRS) Accuracy 4",A2ACoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,4) +MENU_GROUP_COMMAND:New(PlayerGroup,"Military Grid (MGRS) Accuracy 5",A2ACoordinateMenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,5) +end +local MetricsMenu=MENU_GROUP:New(PlayerGroup,"Measures and Weights System",PlayerMenu) +if self:IsMetric()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Imperial (Miles,Feet)",MetricsMenu,self.MenuGroupMWSystem,self,PlayerUnit,PlayerGroup,PlayerName,false) +end +if self:IsImperial()then +MENU_GROUP_COMMAND:New(PlayerGroup,"Metric (Kilometers,Meters)",MetricsMenu,self.MenuGroupMWSystem,self,PlayerUnit,PlayerGroup,PlayerName,true) +end +local MessagesMenu=MENU_GROUP:New(PlayerGroup,"Messages and Reports",PlayerMenu) +local UpdateMessagesMenu=MENU_GROUP:New(PlayerGroup,"Update Messages",MessagesMenu) +MENU_GROUP_COMMAND:New(PlayerGroup,"Off",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,0) +MENU_GROUP_COMMAND:New(PlayerGroup,"5 seconds",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,5) +MENU_GROUP_COMMAND:New(PlayerGroup,"10 seconds",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,10) +MENU_GROUP_COMMAND:New(PlayerGroup,"15 seconds",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,15) +MENU_GROUP_COMMAND:New(PlayerGroup,"30 seconds",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,30) +MENU_GROUP_COMMAND:New(PlayerGroup,"1 minute",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,60) +local InformationMessagesMenu=MENU_GROUP:New(PlayerGroup,"Information Messages",MessagesMenu) +MENU_GROUP_COMMAND:New(PlayerGroup,"5 seconds",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,5) +MENU_GROUP_COMMAND:New(PlayerGroup,"10 seconds",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,10) +MENU_GROUP_COMMAND:New(PlayerGroup,"15 seconds",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,15) +MENU_GROUP_COMMAND:New(PlayerGroup,"30 seconds",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,30) +MENU_GROUP_COMMAND:New(PlayerGroup,"1 minute",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,60) +MENU_GROUP_COMMAND:New(PlayerGroup,"2 minutes",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,"15 seconds",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,15) +MENU_GROUP_COMMAND:New(PlayerGroup,"30 seconds",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,30) +MENU_GROUP_COMMAND:New(PlayerGroup,"1 minute",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,60) +MENU_GROUP_COMMAND:New(PlayerGroup,"2 minutes",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,120) +MENU_GROUP_COMMAND:New(PlayerGroup,"3 minutes",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,"15 seconds",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,15) +MENU_GROUP_COMMAND:New(PlayerGroup,"30 seconds",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,30) +MENU_GROUP_COMMAND:New(PlayerGroup,"1 minute",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,60) +MENU_GROUP_COMMAND:New(PlayerGroup,"2 minutes",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,120) +MENU_GROUP_COMMAND:New(PlayerGroup,"3 minutes",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,"15 seconds",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,15) +MENU_GROUP_COMMAND:New(PlayerGroup,"30 seconds",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,30) +MENU_GROUP_COMMAND:New(PlayerGroup,"1 minute",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,60) +MENU_GROUP_COMMAND:New(PlayerGroup,"2 minutes",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,120) +MENU_GROUP_COMMAND:New(PlayerGroup,"3 minutes",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,180) +return self +end +function SETTINGS:RemovePlayerMenu(PlayerUnit) +if self.PlayerMenu then +self.PlayerMenu:Remove() +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) +self:RemovePlayerMenu(PlayerUnit) +self:SetPlayerMenu(PlayerUnit) +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) +self:RemovePlayerMenu(PlayerUnit) +self:SetPlayerMenu(PlayerUnit) +end +function SETTINGS:MenuGroupLL_DDM_AccuracySystem(PlayerUnit,PlayerGroup,PlayerName,LL_Accuracy) +self.LL_Accuracy=LL_Accuracy +MESSAGE:New(string.format("Settings: A2G LL format accuracy set to %d for player %s.",LL_Accuracy,PlayerName),5):ToGroup(PlayerGroup) +self:RemovePlayerMenu(PlayerUnit) +self:SetPlayerMenu(PlayerUnit) +end +function SETTINGS:MenuGroupMGRS_AccuracySystem(PlayerUnit,PlayerGroup,PlayerName,MGRS_Accuracy) +self.MGRS_Accuracy=MGRS_Accuracy +MESSAGE:New(string.format("Settings: A2G MGRS format accuracy set to %d for player %s.",MGRS_Accuracy,PlayerName),5):ToGroup(PlayerGroup) +self:RemovePlayerMenu(PlayerUnit) +self:SetPlayerMenu(PlayerUnit) +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) +self:RemovePlayerMenu(PlayerUnit) +self:SetPlayerMenu(PlayerUnit) +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 +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.MenuParentPath=MenuParentPath +self.Menus={} +self.MenuCount=0 +self.MenuRemoveParent=false +self.MenuTime=timer.getTime() +return self +end +function MENU_BASE:GetMenu(MenuText) +self:F2({Menu=self.Menus[MenuText]}) +return self.Menus[MenuText] +end +function MENU_BASE:SetRemoveParent(RemoveParent) +self:F2({RemoveParent}) +self.MenuRemoveParent=RemoveParent +return self +end +function MENU_BASE:SetTime(MenuTime) +self.MenuTime=MenuTime +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 debug~=nil then +env.info(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) +local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) +self:F({MenuText,ParentMenu}) +self.MenuText=MenuText +self.ParentMenu=ParentMenu +self.Menus={} +self:T({MenuText}) +self.MenuPath=missionCommands.addSubMenu(MenuText,self.MenuParentPath) +self:T({self.MenuPath}) +if ParentMenu and ParentMenu.Menus then +ParentMenu.Menus[self.MenuPath]=self +end +return self +end +function MENU_MISSION:RemoveSubMenus() +self:F(self.MenuPath) +for MenuID,Menu in pairs(self.Menus)do +Menu:Remove() +end +end +function MENU_MISSION:Remove() +self:F(self.MenuPath) +self:RemoveSubMenus() +missionCommands.removeItem(self.MenuPath) +if self.ParentMenu then +self.ParentMenu.Menus[self.MenuPath]=nil +end +return nil +end +end +do +MENU_MISSION_COMMAND={ +ClassName="MENU_MISSION_COMMAND" +} +function MENU_MISSION_COMMAND:New(MenuText,ParentMenu,CommandMenuFunction,...) +local self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg)) +self.MenuText=MenuText +self.ParentMenu=ParentMenu +self:T({MenuText,CommandMenuFunction,arg}) +self.MenuPath=missionCommands.addCommand(MenuText,self.MenuParentPath,self.MenuCallHandler) +ParentMenu.Menus[self.MenuPath]=self +return self +end +function MENU_MISSION_COMMAND:Remove() +self:F(self.MenuPath) +missionCommands.removeItem(self.MenuPath) +if self.ParentMenu then +self.ParentMenu.Menus[self.MenuPath]=nil +end +return nil +end +end +do +MENU_COALITION={ +ClassName="MENU_COALITION" +} +function MENU_COALITION:New(Coalition,MenuText,ParentMenu) +local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) +self:F({Coalition,MenuText,ParentMenu}) +self.Coalition=Coalition +self.MenuText=MenuText +self.ParentMenu=ParentMenu +self.Menus={} +self:T({MenuText}) +self.MenuPath=missionCommands.addSubMenuForCoalition(Coalition,MenuText,self.MenuParentPath) +self:T({self.MenuPath}) +if ParentMenu and ParentMenu.Menus then +ParentMenu.Menus[self.MenuPath]=self +end +return self +end +function MENU_COALITION:RemoveSubMenus() +self:F(self.MenuPath) +for MenuID,Menu in pairs(self.Menus)do +Menu:Remove() +end +end +function MENU_COALITION:Remove() +self:F(self.MenuPath) +self:RemoveSubMenus() +missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath) +if self.ParentMenu then +self.ParentMenu.Menus[self.MenuPath]=nil +end +return nil +end +end +do +MENU_COALITION_COMMAND={ +ClassName="MENU_COALITION_COMMAND" +} +function MENU_COALITION_COMMAND:New(Coalition,MenuText,ParentMenu,CommandMenuFunction,...) +local self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg)) +self.MenuCoalition=Coalition +self.MenuText=MenuText +self.ParentMenu=ParentMenu +self:T({MenuText,CommandMenuFunction,arg}) +self.MenuPath=missionCommands.addCommandForCoalition(self.MenuCoalition,MenuText,self.MenuParentPath,self.MenuCallHandler) +ParentMenu.Menus[self.MenuPath]=self +return self +end +function MENU_COALITION_COMMAND:Remove() +self:F(self.MenuPath) +missionCommands.removeItemForCoalition(self.MenuCoalition,self.MenuPath) +if self.ParentMenu then +self.ParentMenu.Menus[self.MenuPath]=nil +end +return nil +end +end +do +local _MENUCLIENTS={} +MENU_CLIENT={ +ClassName="MENU_CLIENT" +} +function MENU_CLIENT:New(Client,MenuText,ParentMenu) +local MenuParentPath={} +if ParentMenu~=nil then +MenuParentPath=ParentMenu.MenuPath +end +local self=BASE:Inherit(self,MENU_BASE:New(MenuText,MenuParentPath)) +self:F({Client,MenuText,ParentMenu}) +self.MenuClient=Client +self.MenuClientGroupID=Client:GetClientGroupID() +self.MenuParentPath=MenuParentPath +self.MenuText=MenuText +self.ParentMenu=ParentMenu +self.Menus={} +if not _MENUCLIENTS[self.MenuClientGroupID]then +_MENUCLIENTS[self.MenuClientGroupID]={} +end +local MenuPath=_MENUCLIENTS[self.MenuClientGroupID] +self:T({Client:GetClientGroupName(),MenuPath[table.concat(MenuParentPath)],MenuParentPath,MenuText}) +local MenuPathID=table.concat(MenuParentPath).."/"..MenuText +if MenuPath[MenuPathID]then +missionCommands.removeItemForGroup(self.MenuClient:GetClientGroupID(),MenuPath[MenuPathID]) +end +self.MenuPath=missionCommands.addSubMenuForGroup(self.MenuClient:GetClientGroupID(),MenuText,MenuParentPath) +MenuPath[MenuPathID]=self.MenuPath +self:T({Client:GetClientGroupName(),self.MenuPath}) +if ParentMenu and ParentMenu.Menus then +ParentMenu.Menus[self.MenuPath]=self +end +return self +end +function MENU_CLIENT:RemoveSubMenus() +self:F(self.MenuPath) +for MenuID,Menu in pairs(self.Menus)do +Menu:Remove() +end +end +function MENU_CLIENT:Remove() +self:F(self.MenuPath) +self:RemoveSubMenus() +if not _MENUCLIENTS[self.MenuClientGroupID]then +_MENUCLIENTS[self.MenuClientGroupID]={} +end +local MenuPath=_MENUCLIENTS[self.MenuClientGroupID] +if MenuPath[table.concat(self.MenuParentPath).."/"..self.MenuText]then +MenuPath[table.concat(self.MenuParentPath).."/"..self.MenuText]=nil +end +missionCommands.removeItemForGroup(self.MenuClient:GetClientGroupID(),self.MenuPath) +self.ParentMenu.Menus[self.MenuPath]=nil +return nil +end +MENU_CLIENT_COMMAND={ +ClassName="MENU_CLIENT_COMMAND" +} +function MENU_CLIENT_COMMAND:New(Client,MenuText,ParentMenu,CommandMenuFunction,...) +local MenuParentPath={} +if ParentMenu~=nil then +MenuParentPath=ParentMenu.MenuPath +end +local self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,MenuParentPath,CommandMenuFunction,arg)) +self.MenuClient=Client +self.MenuClientGroupID=Client:GetClientGroupID() +self.MenuParentPath=MenuParentPath +self.MenuText=MenuText +self.ParentMenu=ParentMenu +if not _MENUCLIENTS[self.MenuClientGroupID]then +_MENUCLIENTS[self.MenuClientGroupID]={} +end +local MenuPath=_MENUCLIENTS[self.MenuClientGroupID] +self:T({Client:GetClientGroupName(),MenuPath[table.concat(MenuParentPath)],MenuParentPath,MenuText,CommandMenuFunction,arg}) +local MenuPathID=table.concat(MenuParentPath).."/"..MenuText +if MenuPath[MenuPathID]then +missionCommands.removeItemForGroup(self.MenuClient:GetClientGroupID(),MenuPath[MenuPathID]) +end +self.MenuPath=missionCommands.addCommandForGroup(self.MenuClient:GetClientGroupID(),MenuText,MenuParentPath,self.MenuCallHandler) +MenuPath[MenuPathID]=self.MenuPath +if ParentMenu and ParentMenu.Menus then +ParentMenu.Menus[self.MenuPath]=self +end +return self +end +function MENU_CLIENT_COMMAND:Remove() +self:F(self.MenuPath) +if not _MENUCLIENTS[self.MenuClientGroupID]then +_MENUCLIENTS[self.MenuClientGroupID]={} +end +local MenuPath=_MENUCLIENTS[self.MenuClientGroupID] +if MenuPath[table.concat(self.MenuParentPath).."/"..self.MenuText]then +MenuPath[table.concat(self.MenuParentPath).."/"..self.MenuText]=nil +end +missionCommands.removeItemForGroup(self.MenuClient:GetClientGroupID(),self.MenuPath) +self.ParentMenu.Menus[self.MenuPath]=nil +return nil +end +end +do +local _MENUGROUPS={} +MENU_GROUP={ +ClassName="MENU_GROUP" +} +function MENU_GROUP:New(MenuGroup,MenuText,ParentMenu) +MenuGroup._Menus=MenuGroup._Menus or{} +local Path=(ParentMenu and(table.concat(ParentMenu.MenuPath or{},"@").."@"..MenuText))or MenuText +if MenuGroup._Menus[Path]then +self=MenuGroup._Menus[Path] +else +self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu)) +MenuGroup._Menus[Path]=self +self.MenuGroup=MenuGroup +self.Path=Path +self.MenuGroupID=MenuGroup:GetID() +self.MenuText=MenuText +self.ParentMenu=ParentMenu +self:T({"Adding Menu ",MenuText,self.MenuParentPath}) +self.MenuPath=missionCommands.addSubMenuForGroup(self.MenuGroupID,MenuText,self.MenuParentPath) +if self.ParentMenu and self.ParentMenu.Menus then +self.ParentMenu.Menus[MenuText]=self +self:F({self.ParentMenu.Menus,MenuText}) +self.ParentMenu.MenuCount=self.ParentMenu.MenuCount+1 +end +end +return self +end +function MENU_GROUP:RemoveSubMenus(MenuTime,MenuTag) +self:T({"Removing Group SubMenus:",MenuTime,MenuTag,self.MenuGroup:GetName(),self.MenuPath}) +for MenuText,Menu in pairs(self.Menus)do +Menu:Remove(MenuTime,MenuTag) +end +end +function MENU_GROUP:Remove(MenuTime,MenuTag) +self:RemoveSubMenus(MenuTime,MenuTag) +if not MenuTime or self.MenuTime~=MenuTime then +if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then +if self.MenuGroup._Menus[self.Path]then +self=self.MenuGroup._Menus[self.Path] +missionCommands.removeItemForGroup(self.MenuGroupID,self.MenuPath) +if self.ParentMenu then +self.ParentMenu.Menus[self.MenuText]=nil +self.ParentMenu.MenuCount=self.ParentMenu.MenuCount-1 +if self.ParentMenu.MenuCount==0 then +if self.MenuRemoveParent==true then +self:T2("Removing Parent Menu ") +self.ParentMenu:Remove() +end +end +end +end +self:T({"Removing Group Menu:",MenuGroup=self.MenuGroup:GetName()}) +self.MenuGroup._Menus[self.Path]=nil +self=nil +end +end +return nil +end +MENU_GROUP_COMMAND={ +ClassName="MENU_GROUP_COMMAND" +} +function MENU_GROUP_COMMAND:New(MenuGroup,MenuText,ParentMenu,CommandMenuFunction,...) +MenuGroup._Menus=MenuGroup._Menus or{} +local Path=(ParentMenu and(table.concat(ParentMenu.MenuPath or{},"@").."@"..MenuText))or MenuText +if MenuGroup._Menus[Path]then +self=MenuGroup._Menus[Path] +self:SetCommandMenuFunction(CommandMenuFunction) +self:SetCommandMenuArguments(arg) +return self +end +self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg)) +MenuGroup._Menus[Path]=self +self.Path=Path +self.MenuGroup=MenuGroup +self.MenuGroupID=MenuGroup:GetID() +self.MenuText=MenuText +self.ParentMenu=ParentMenu +self:F({"Adding Group Command Menu:",MenuGroup=MenuGroup:GetName(),MenuText=MenuText,MenuPath=self.MenuParentPath}) +self.MenuPath=missionCommands.addCommandForGroup(self.MenuGroupID,MenuText,self.MenuParentPath,self.MenuCallHandler) +if self.ParentMenu and self.ParentMenu.Menus then +self.ParentMenu.Menus[MenuText]=self +self.ParentMenu.MenuCount=self.ParentMenu.MenuCount+1 +self:F2({ParentMenu.Menus,MenuText}) +end +return self +end +function MENU_GROUP_COMMAND:Remove(MenuTime,MenuTag) +if not MenuTime or self.MenuTime~=MenuTime then +if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then +if self.MenuGroup._Menus[self.Path]then +self=self.MenuGroup._Menus[self.Path] +missionCommands.removeItemForGroup(self.MenuGroupID,self.MenuPath) +self.ParentMenu.Menus[self.MenuText]=nil +self.ParentMenu.MenuCount=self.ParentMenu.MenuCount-1 +if self.ParentMenu.MenuCount==0 then +if self.MenuRemoveParent==true then +self:T2("Removing Parent Menu ") +self.ParentMenu:Remove() +end +end +self.MenuGroup._Menus[self.Path]=nil +self=nil +end +end +end +return nil +end +end +ZONE_BASE={ +ClassName="ZONE_BASE", +ZoneName="", +ZoneProbability=1, +} +function ZONE_BASE:New(ZoneName) +local self=BASE:Inherit(self,BASE:New()) +self:F(ZoneName) +self.ZoneName=ZoneName +return self +end +function ZONE_BASE:GetName() +self:F2() +return self.ZoneName +end +function ZONE_BASE:IsVec2InZone(Vec2) +self:F2(Vec2) +return false +end +function ZONE_BASE:IsVec3InZone(Vec3) +self:F2(Vec3) +local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z}) +return InZone +end +function ZONE_BASE:IsPointVec2InZone(PointVec2) +self:F2(PointVec2) +local InZone=self:IsVec2InZone(PointVec2:GetVec2()) +return InZone +end +function ZONE_BASE:IsPointVec3InZone(PointVec3) +self:F2(PointVec3) +local InZone=self:IsPointVec2InZone(PointVec3) +return InZone +end +function ZONE_BASE:GetVec2() +self:F2(self.ZoneName) +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:GetCoordinate() +self:F2(self.ZoneName) +local Vec2=self:GetVec2() +local Coordinate=COORDINATE:NewFromVec2(Vec2) +self:T2({Coordinate}) +return Coordinate +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) +local PointVec3=COORDINATE:NewFromVec3(Vec3) +self:T2({PointVec3}) +return PointVec3 +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:F2(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: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) +self:F2(SmokeColor) +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() +POINT_VEC2:New(Point.x,Point.y):Smoke(SmokeColor) +end +return self +end +function ZONE_RADIUS:FlareZone(FlareColor,Points,Azimuth) +self:F2({FlareColor,Azimuth}) +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() +POINT_VEC2:New(Point.x,Point.y):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() +self.Coalitions={} +local ZoneCoord=self:GetCoordinate() +local ZoneRadius=self:GetRadius() +self:E({ZoneCoord=ZoneCoord,ZoneRadius=ZoneRadius,ZoneCoordLL=ZoneCoord:ToStringLLDMS()}) +local SphereSearch={ +id=world.VolumeType.SPHERE, +params={ +point=ZoneCoord:GetVec3(), +radius=ZoneRadius, +} +} +local function EvaluateZone(ZoneDCSUnit) +if ZoneDCSUnit:isExist()then +local CategoryDCSUnit=ZoneDCSUnit:getCategory() +if(CategoryDCSUnit==Object.Category.UNIT and ZoneDCSUnit:isActive())or +CategoryDCSUnit==Object.Category.STATIC then +local CoalitionDCSUnit=ZoneDCSUnit:getCoalition() +self.Coalitions[CoalitionDCSUnit]=true +self:E({Name=ZoneDCSUnit:getName(),Coalition=CoalitionDCSUnit}) +end +end +return true +end +world.searchObjects({Object.Category.UNIT,Object.Category.STATIC},SphereSearch,EvaluateZone) +end +function ZONE_RADIUS:CountCoalitions() +local Count=0 +for CoalitionID,Coalition in pairs(self.Coalitions)do +Count=Count+1 +end +return Count +end +function ZONE_RADIUS:IsAllInZoneOfCoalition(Coalition) +return self:CountCoalitions()==1 and self.Coalitions[Coalition]==true +end +function ZONE_RADIUS:IsAllInZoneOfOtherCoalition(Coalition) +self:E({Coalitions=self.Coalitions,Count=self:CountCoalitions()}) +return self:CountCoalitions()==1 and self.Coalitions[Coalition]==nil +end +function ZONE_RADIUS:IsSomeInZoneOfCoalition(Coalition) +return self:CountCoalitions()>1 and self.Coalitions[Coalition]==true +end +function ZONE_RADIUS:IsNoneInZoneOfCoalition(Coalition) +return self.Coalitions[Coalition]==nil +end +function ZONE_RADIUS:GetCoalition() +local Count=0 +local ReturnCoalition=nil +for CoalitionID,Coalition in pairs(self.Coalitions)do +Count=Count+1 +ReturnCoalition=CoalitionID +end +if Count~=1 then +ReturnCoalition=nil +end +return ReturnCoalition +end +function ZONE_RADIUS:SearchZone(EvaluateFunction) +local SearchZoneResult=true +local ZoneCoord=self:GetCoordinate() +local ZoneRadius=self:GetRadius() +self:E({ZoneCoord=ZoneCoord,ZoneRadius=ZoneRadius,ZoneCoordLL=ZoneCoord:ToStringLLDMS()}) +local SphereSearch={ +id=world.VolumeType.SPHERE, +params={ +point=ZoneCoord:GetVec3(), +radius=ZoneRadius/2, +} +} +local function EvaluateZone(ZoneDCSUnit) +env.info(ZoneDCSUnit:getName()) +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()) +self:T3({PointVec2}) +return PointVec2 +end +function ZONE_RADIUS:GetRandomPointVec3(inner,outer) +self:F(self.ZoneName,inner,outer) +local PointVec3=POINT_VEC3:NewFromVec2(self:GetRandomVec2()) +self:T3({PointVec3}) +return PointVec3 +end +function ZONE_RADIUS:GetRandomCoordinate(inner,outer) +self:F(self.ZoneName,inner,outer) +local Coordinate=COORDINATE:NewFromVec2(self:GetRandomVec2()) +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 +ZONE_UNIT={ +ClassName="ZONE_UNIT", +} +function ZONE_UNIT:New(ZoneName,ZoneUNIT,Radius) +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() +return self +end +function ZONE_UNIT:GetVec2() +self:F2(self.ZoneName) +local ZoneVec2=self.ZoneUNIT:GetVec2() +if ZoneVec2 then +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.ZoneUNIT: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 +return self +end +function ZONE_GROUP:GetVec2() +self:F(self.ZoneName) +local ZoneVec2=self._.ZoneGROUP:GetVec2() +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}) +local i=0 +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 +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:E({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) +self:F2(SmokeColor) +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) +POINT_VEC2:New(PointX,PointY):Smoke(SmokeColor) +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 +for ZoneID,ZoneData in pairs(env.mission.triggers.zones)do +local ZoneName=ZoneData.name +self.ZONENAMES[ZoneName]=ZoneName +end +return self +end +SET_BASE={ +ClassName="SET_BASE", +Filter={}, +Set={}, +List={}, +Index={}, +} +function SET_BASE:New(Database) +local self=BASE:Inherit(self,BASE:New()) +self.Database=Database +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:_Find(ObjectName) +local ObjectFound=self.Set[ObjectName] +return ObjectFound +end +function SET_BASE:GetSet() +self:F2() +return self.Set +end +function SET_BASE:Add(ObjectName,Object) +self:F(ObjectName) +self.Set[ObjectName]=Object +table.insert(self.Index,ObjectName) +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:Remove(ObjectName) +local Object=self.Set[ObjectName] +self:F3({ObjectName,Object}) +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 +end +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: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:E({"Adding Object:",ObjectName}) +self:Add(ObjectName,Object) +end +end +self:HandleEvent(EVENTS.Birth,self._EventOnBirth) +self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash) +self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash) +self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventOnPlayerEnterUnit) +self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventOnPlayerLeaveUnit) +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:DistanceFromVec2(ObjectData:GetVec2()) +else +local Distance=PointVec2:DistanceFromVec2(ObjectData:GetVec2()) +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: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=true +if self.Filter.Coalitions then +local MUnitCoalition=false +for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do +self:E({"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 +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 +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() +end +return self +end +function SET_STATIC:AddInDatabase(Event) +self:F3({Event}) +if Event.IniObjectCategory==Object.Category.STATIC then +if not self.Database[Event.IniDCSStaticName]then +self.Database[Event.IniDCSStaticName]=STATIC:Register(Event.IniDCSStaticName) +self:T3(self.Database[Event.IniDCSStaticName]) +end +end +return Event.IniDCSStaticName,self.Database[Event.IniDCSStaticName] +end +function SET_STATIC:FindInDatabase(Event) +self:F2({Event.IniDCSStaticName,self.Set[Event.IniDCSStaticName],Event}) +return Event.IniDCSStaticName,self.Set[Event.IniDCSStaticName] +end +do +function SET_STATIC:IsPatriallyInZone(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.Set) +return self +end +end +function SET_STATIC:ForEachStatic(IteratorFunction,...) +self:F2(arg) +self:ForEach(IteratorFunction,arg,self.Set) +return self +end +function SET_STATIC:ForEachStaticCompletelyInZone(ZoneObject,IteratorFunction,...) +self:F2(arg) +self:ForEach(IteratorFunction,arg,self.Set, +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.Set, +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: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 +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)) +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:FilterStart() +if _DATABASE then +self:_FilterStart() +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.Set) +return self +end +function SET_CLIENT:ForEachClientInZone(ZoneObject,IteratorFunction,...) +self:F2(arg) +self:ForEach(IteratorFunction,arg,self.Set, +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.Set, +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.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 +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: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.AirbaseName) +end +return self +end +function SET_AIRBASE:FindAirbase(AirbaseName) +local AirbaseFound=self.Set[AirbaseName] +return AirbaseFound +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:_FilterStart() +end +return self +end +function SET_AIRBASE:AddInDatabase(Event) +self:F3({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.Set) +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 +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: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() +end +self:HandleEvent(EVENTS.NewCargo) +self:HandleEvent(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.Set) +return self +end +function SET_CARGO:FindNearestCargoFromPointVec2(PointVec2) +self:F2(PointVec2) +local NearestCargo=self:FindNearestObjectFromPointVec2(PointVec2) +return NearestCargo +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:T({"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:T({"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:T({"Evaluated Prefix",MCargoPrefix}) +MCargoInclude=MCargoInclude and MCargoPrefix +end +end +self:T2(MCargoInclude) +return MCargoInclude +end +function SET_CARGO:OnEventNewCargo(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:Remove(Cargo.Name) +end +end +end +do +COORDINATE={ +ClassName="COORDINATE", +} +function COORDINATE:New(x,y,z) +local self=BASE:Inherit(self,BASE:New()) +self.x=x +self.y=y +self.z=z +return self +end +function COORDINATE:NewFromVec2(Vec2,LandHeightAdd) +local LandHeight=land.getHeight(Vec2) +LandHeightAdd=LandHeightAdd or 0 +LandHeight=LandHeight+LandHeightAdd +local self=self:New(Vec2.x,LandHeight,Vec2.y) +self:F2(self) +return self +end +function COORDINATE:NewFromVec3(Vec3) +local self=self:New(Vec3.x,Vec3.y,Vec3.z) +self:F2(self) +return self +end +function COORDINATE:GetVec3() +return{x=self.x,y=self.y,z=self.z} +end +function COORDINATE:GetVec2() +return{x=self.x,y=self.z} +end +function COORDINATE:DistanceFromVec2(Vec2Reference) +self:F2(Vec2Reference) +local Distance=((Vec2Reference.x-self.x)^2+(Vec2Reference.y-self.z)^2)^0.5 +self:T2(Distance) +return Distance +end +function COORDINATE:Translate(Distance,Angle) +local SX=self.x +local SY=self.z +local Radians=Angle/180*math.pi +local TX=Distance*math.cos(Radians)+SX +local TY=Distance*math.sin(Radians)+SY +return COORDINATE:NewFromVec2({x=TX,y=TY}) +end +function COORDINATE:GetRandomVec2InRadius(OuterRadius,InnerRadius) +self:F2({OuterRadius,InnerRadius}) +local Theta=2*math.pi*math.random() +local Radials=math.random()+math.random() +if Radials>1 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: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: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:Get2DDistance(TargetCoordinate) +local TargetVec3=TargetCoordinate:GetVec3() +local SourceVec3=self:GetVec3() +return((TargetVec3.x-SourceVec3.x)^2+(TargetVec3.z-SourceVec3.z)^2)^0.5 +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) +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) +local Settings=Settings or _SETTINGS +local DistanceText +if Settings:IsMetric()then +DistanceText=" for "..UTILS.Round(Distance/1000,2).." km" +else +DistanceText=" for "..UTILS.Round(UTILS.MetersToNM(Distance),2).." miles" +end +return DistanceText +end +function COORDINATE:GetAltitudeText(Settings) +local Altitude=self.y +local Settings=Settings or _SETTINGS +if Altitude~=0 then +if Settings:IsMetric()then +return" at "..UTILS.Round(self.y,-3).." meters" +else +return" at "..UTILS.Round(UTILS.MetersToFeet(self.y),-3).." feet" +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) +local Settings=Settings or _SETTINGS +local BearingText=self:GetBearingText(AngleRadians,0,Settings) +local DistanceText=self:GetDistanceText(Distance,Settings) +local BRText=BearingText..DistanceText +return BRText +end +function COORDINATE:GetBRAText(AngleRadians,Distance,Settings) +local Settings=Settings or _SETTINGS +local BearingText=self:GetBearingText(AngleRadians,0,Settings) +local DistanceText=self:GetDistanceText(Distance,Settings) +local AltitudeText=self:GetAltitudeText(Settings) +local BRAText=BearingText..DistanceText..AltitudeText +return BRAText +end +function COORDINATE:Translate(Distance,Angle) +local SX=self.x +local SZ=self.z +local Radians=Angle/180*math.pi +local TX=Distance*math.cos(Radians)+SX +local TZ=Distance*math.sin(Radians)+SZ +return COORDINATE:New(TX,self.y,TZ) +end +function COORDINATE:WaypointAir(AltType,Type,Action,Speed,SpeedLocked) +self:F2({AltType,Type,Action,Speed,SpeedLocked}) +local RoutePoint={} +RoutePoint.x=self.x +RoutePoint.y=self.z +RoutePoint.alt=self.y +RoutePoint.alt_type=AltType or"RADIO" +RoutePoint.type=Type or nil +RoutePoint.action=Action or nil +RoutePoint.speed=(Speed and Speed/3.6)or(500/3.6) +RoutePoint.speed_locked=true +RoutePoint.task={} +RoutePoint.task.id="ComboTask" +RoutePoint.task.params={} +RoutePoint.task.params.tasks={} +return RoutePoint +end +function COORDINATE:WaypointGround(Speed,Formation) +self:F2({Formation,Speed}) +local RoutePoint={} +RoutePoint.x=self.x +RoutePoint.y=self.z +RoutePoint.action=Formation or"" +RoutePoint.speed=(Speed or 999)/3.6 +RoutePoint.speed_locked=true +RoutePoint.task={} +RoutePoint.task.id="ComboTask" +RoutePoint.task.params={} +RoutePoint.task.params.tasks={} +return RoutePoint +end +function COORDINATE:Explosion(ExplosionIntensity) +self:F2({ExplosionIntensity}) +trigger.action.explosion(self:GetVec3(),ExplosionIntensity) +end +function COORDINATE:IlluminationBomb() +self:F2() +trigger.action.illuminationBomb(self:GetVec3()) +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: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) +local MarkID=UTILS.GetMarkID() +trigger.action.markToAll(MarkID,MarkText,self:GetVec3()) +return MarkID +end +function COORDINATE:MarkToCoalition(MarkText,Coalition) +local MarkID=UTILS.GetMarkID() +trigger.action.markToCoalition(MarkID,MarkText,self:GetVec3(),Coalition) +return MarkID +end +function COORDINATE:MarkToCoalitionRed(MarkText) +return self:MarkToCoalition(MarkText,coalition.side.RED) +end +function COORDINATE:MarkToCoalitionBlue(MarkText) +return self:MarkToCoalition(MarkText,coalition.side.BLUE) +end +function COORDINATE:MarkToGroup(MarkText,MarkGroup) +local MarkID=UTILS.GetMarkID() +trigger.action.markToGroup(MarkID,MarkText,self:GetVec3(),MarkGroup:GetID()) +return MarkID +end +function COORDINATE:RemoveMark(MarkID) +trigger.action.removeMark(MarkID) +end +end +function COORDINATE:IsLOS(ToCoordinate) +local FromVec3=self:GetVec3() +FromVec3.y=FromVec3.y+2 +local ToVec3=ToCoordinate:GetVec3() +ToVec3.y=ToVec3.y+2 +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: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) +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) +end +function COORDINATE:ToStringBULLS(Coalition,Settings) +local TargetCoordinate=COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition)) +local DirectionVec3=self:GetDirectionVec3(TargetCoordinate) +local AngleRadians=self:GetAngleRadians(DirectionVec3) +local Distance=self:Get2DDistance(TargetCoordinate) +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: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:E({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:F({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) +self:F({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) +else +return self:ToStringMGRS(Settings) +end +end +if Settings:IsA2A_BULLS()then +local Coalition=Controllable:GetCoalition() +return self:ToStringBULLS(Coalition,Settings) +end +if Settings:IsA2A_LL_DMS()then +return self:ToStringLLDMS(Settings) +end +if Settings:IsA2A_LL_DDM()then +return self:ToStringLLDDM(Settings) +end +if Settings:IsA2A_MGRS()then +return self:ToStringMGRS(Settings) +end +return nil +end +function COORDINATE:ToString(Controllable,Settings,Task) +self:F({Controllable=Controllable and Controllable:GetName()}) +local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS +local ModeA2A=true +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 +end +end +else +local IsAir=Controllable and Controllable:IsAirPlane()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 +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 +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) +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.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) +local self=BASE:Inherit(self,BASE:New()) +self:F({MessageText}) +self.MessageType=MessageType +self.MessageTime=timer.getTime() +self.MessageText=MessageText:gsub("^\n","",1):gsub("\n$","",1) +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=self.MessageType..": " +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) +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=self.MessageType..": " +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) +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=self.MessageType..": " +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.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$",""),self.MessageDuration) +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() +self:F() +if self.MessageType then +local Settings=Settings or _SETTINGS +self.MessageDuration=Settings:GetMessageTime(self.MessageType) +self.MessageCategory=self.MessageType..": " +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) +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(FsmT) +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() +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: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(handler,params,EventName) +local ErrorHandler=function(errmsg) +env.info("Error in SCHEDULER function:"..errmsg) +if debug~=nil then +env.info(debug.traceback()) +end +return errmsg +end +if self[handler]then +self:T2("Calling "..handler) +self._EventSchedules[EventName]=nil +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.Controllable then +self:T("FSM Transition for "..self.Controllable.ControllableName.." :"..self.current.." --> "..EventName.." --> "..to) +else +self:T("FSM Transition:"..self.current.." --> "..EventName.." --> "..to) +end +if(self:_call_handler("onbefore"..EventName,params,EventName)==false) +or(self:_call_handler("OnBefore"..EventName,params,EventName)==false) +or(self:_call_handler("onleave"..from,params,EventName)==false) +or(self:_call_handler("OnLeave"..from,params,EventName)==false)then +self:T("Cancel Transition") +return false +end +self.current=to +local execute=true +local subtable=self:_gosub(from,EventName) +for _,sub in pairs(subtable)do +self:T("calling sub start event: "..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:F2({"end state: ",fsmparent,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("onstatechange",params,EventName) +fsmparent[Event](fsmparent) +execute=false +end +if execute then +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("onstatechange",params,EventName) +end +else +self:T("Cannot execute transition.") +self:T({From=self.current,Event=EventName,To=to,Can=Can}) +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) +self._EventSchedules[EventName]=CallID +else +end +else +CallID=self.CallScheduler:Schedule(self,self._handler,{EventName,...},DelaySeconds or 1) +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 +self:T({state=Current,endstates=self.endstates,endstate=self.endstates[Current]}) +FSMParent.current=Current +local ParentFrom=FSMParent.current +self:T(ParentFrom) +self:T(self.ReturnEvents) +local Event=self.ReturnEvents[Current] +self:T({ParentFrom,Event,self.ReturnEvents}) +if Event then +return FSMParent,Event +else +self:T({"Could not find parent event name for state ",ParentFrom}) +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: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] +self:F3({self.current,Event}) +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(FSMT,Controllable) +local self=BASE:Inherit(self,FSM:New(FSMT)) +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(handler,params,EventName) +local ErrorHandler=function(errmsg) +env.info("Error in SCHEDULER function:"..errmsg) +if debug~=nil then +env.info(debug.traceback()) +end +return errmsg +end +if self[handler]then +self:F3("Calling "..handler) +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(handler,params,EventName) +local ErrorHandler=function(errmsg) +env.info("Error in FSM_PROCESS call handler:"..errmsg) +if debug~=nil then +env.info(debug.traceback()) +end +return errmsg +end +if self[handler]then +self:F3("Calling "..handler) +self._EventSchedules[EventName]=nil +local Result,Value=xpcall(function()return self[handler](self,self.Controllable,self.Task,unpack(params))end,ErrorHandler) +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:onenterAssigned(ProcessUnit) +self:T("Assign") +self.Task:Assign() +end +function FSM_PROCESS:onenterFailed(ProcessUnit) +self:T("Failed") +self.Task:Fail() +end +function FSM_PROCESS:onstatechange(ProcessUnit,Task,From,Event,To,Dummy) +self:T({ProcessUnit:GetName(),From,Event,To,Dummy,self:IsTrace()}) +if self:IsTrace()then +end +self:T({Scores=self._Scores,To=To}) +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(FSMT) +local self=BASE:Inherit(self,FSM_CONTROLLABLE:New(FSMT)) +self["onstatechange"]=self.OnStateChange +return self +end +function FSM_TASK:_call_handler(handler,params,EventName) +if self[handler]then +self:T("Calling "..handler) +self._EventSchedules[EventName]=nil +return self[handler](self,unpack(params)) +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(handler,params,EventName) +if self[handler]then +self:T("Calling "..handler) +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=true, +} +function RADIO:New(Positionable) +local self=BASE:Inherit(self,BASE:New()) +self.Loop=true +self:F(Positionable) +if Positionable:GetPointVec2()then +self.Positionable=Positionable +return self +end +self:E({"The passed positionable is invalid, no RADIO created",Positionable}) +return nil +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 ?",self.FileName}) +return self +end +function RADIO:SetFrequency(Frequency) +self:F2(Frequency) +if type(Frequency)=="number"then +if(Frequency>=30 and Frequency<88)or(Frequency>=108 and Frequency<152)or(Frequency>=225 and Frequency<400)then +self.Frequency=Frequency*1000000 +if self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP"then +self.Positionable:SetCommand({ +id="SetFrequency", +params={ +frequency=self.Frequency, +modulation=self.Modulation, +} +}) +end +return self +end +end +self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.",self.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)) +return self +end +self:E({"Power is invalid. Power unchanged.",self.Power}) +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 +if math.floor(math.abs(SubtitleDuration))==SubtitleDuration then +self.SubtitleDuration=SubtitleDuration +return self +end +end +self.SubtitleDuration=0 +self:E({"SubtitleDuration is invalid. SubtitleDuration reset.",self.SubtitleDuration}) +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 Subtitle then self:SetSubtitle(Subtitle)end +if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration)end +if Frequency then self:SetFrequency(Frequency)end +if Modulation then self:SetModulation(Modulation)end +if Loop then self:SetLoop(Loop)end +return self +end +function RADIO:Broadcast() +self:F() +if self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP"then +self:T2("Broadcasting from a UNIT or a GROUP") +self.Positionable:SetCommand({ +id="TransmitMessage", +params={ +file=self.FileName, +duration=self.SubtitleDuration, +subtitle=self.Subtitle, +loop=self.Loop, +} +}) +else +self:T2("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 +self.Positionable:SetCommand({ +id="StopTransmission", +params={} +}) +else +trigger.action.stopRadioTransmission(tostring(self.ID)) +end +return self +end +BEACON={ +ClassName="BEACON", +} +function BEACON:New(Positionable) +local self=BASE:Inherit(self,BASE:New()) +self:F(Positionable) +if Positionable:GetPointVec2()then +self.Positionable=Positionable +return self +end +self:E({"The passed positionable is invalid, no BEACON created",Positionable}) +return nil +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 +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)) +end +SPAWNSTATIC={ +ClassName="SPAWNSTATIC", +} +function SPAWNSTATIC:NewFromStatic(SpawnTemplatePrefix,CountryID) +local self=BASE:Inherit(self,BASE:New()) +self:F({SpawnTemplatePrefix}) +local TemplateStatic=StaticObject.getByName(SpawnTemplatePrefix) +if TemplateStatic then +self.SpawnTemplatePrefix=SpawnTemplatePrefix +self.CountryID=CountryID +self.SpawnIndex=0 +else +error("SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'") +end +self:SetEventPriority(5) +return self +end +function SPAWNSTATIC:NewFromType(SpawnTypeName,SpawnShapeName,SpawnCategory,CountryID) +local self=BASE:Inherit(self,BASE:New()) +self:F({SpawnTypeName}) +self.SpawnTypeName=SpawnTypeName +self.CountryID=CountryID +self.SpawnIndex=0 +self:SetEventPriority(5) +return self +end +function SPAWNSTATIC:SpawnFromPointVec2(PointVec2,Heading,NewName) +self:F({PointVec2,Heading,NewName}) +local CountryName=_DATABASE.COUNTRY_NAME[self.CountryID] +local StaticTemplate=_DATABASE:GetStaticUnitTemplate(self.SpawnTemplatePrefix) +StaticTemplate.x=PointVec2:GetLat() +StaticTemplate.y=PointVec2:GetLon() +StaticTemplate.name=NewName or string.format("%s#%05d",self.SpawnTemplatePrefix,self.SpawnIndex) +StaticTemplate.heading=(Heading/180)*math.pi +StaticTemplate.CountryID=nil +StaticTemplate.CoalitionID=nil +StaticTemplate.CategoryID=nil +local Static=coalition.addStaticObject(self.CountryID,StaticTemplate) +self.SpawnIndex=self.SpawnIndex+1 +return Static +end +function SPAWNSTATIC:SpawnFromZone(Zone,Heading,NewName) +self:F({Zone,Heading,NewName}) +local Static=self:SpawnFromPointVec2(Zone:GetPointVec2(),Heading,NewName) +return Static +end +CARGOS={} +do +CARGO={ +ClassName="CARGO", +Type=nil, +Name=nil, +Weight=nil, +CargoObject=nil, +CargoCarrier=nil, +Representable=false, +Slingloadable=false, +Moveable=false, +Containable=false, +} +function CARGO:New(Type,Name,Weight) +local self=BASE:Inherit(self,FSM:New()) +self:F({Type,Name,Weight}) +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.Type=Type +self.Name=Name +self.Weight=Weight +self.CargoObject=nil +self.CargoCarrier=nil +self.Representable=false +self.Slingloadable=false +self.Moveable=false +self.Containable=false +self:SetDeployed(false) +self.CargoScheduler=SCHEDULER:New() +CARGOS[self.Name]=self +return self +end +function CARGO:Destroy() +if self.CargoObject then +self.CargoObject:Destroy() +end +self:Destroyed() +end +function CARGO:GetName() +return self.Name +end +function CARGO:GetObjectName() +if self:IsLoaded()then +return self.CargoCarrier:GetName() +else +return self.CargoObject:GetName() +end +end +function CARGO:GetType() +return self.Type +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:IsUnLoaded() +return self:Is("UnLoaded") +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,Range) +self:F2() +if self:IsUnLoaded()then +if Range then +trigger.action.smoke(self.CargoObject:GetRandomVec3(Range),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:IsInZone(Zone) +self:F({Zone}) +if self:IsLoaded()then +return Zone:IsPointVec2InZone(self.CargoCarrier:GetPointVec2()) +else +self:F({Size=self.CargoObject:GetSize(),Units=self.CargoObject:GetUnits()}) +if self.CargoObject:GetSize()~=0 then +return Zone:IsPointVec2InZone(self.CargoObject:GetPointVec2()) +else +return false +end +end +return nil +end +function CARGO:IsNear(PointVec2,NearRadius) +self:F({PointVec2,NearRadius}) +local Distance=PointVec2:DistanceFromPointVec2(self.CargoObject:GetPointVec2()) +self:T(Distance) +if Distance<=NearRadius then +return true +else +return false +end +end +function CARGO:GetPointVec2() +return self.CargoObject:GetPointVec2() +end +function CARGO:GetCoordinate() +return self.CargoObject:GetCoordinate() +end +function CARGO:SetWeight(Weight) +self.Weight=Weight +return self +end +end +do +CARGO_REPRESENTABLE={ +ClassName="CARGO_REPRESENTABLE" +} +function CARGO_REPRESENTABLE:New(CargoObject,Type,Name,Weight,ReportRadius,NearRadius) +local self=BASE:Inherit(self,CARGO:New(Type,Name,Weight,ReportRadius,NearRadius)) +self:F({Type,Name,Weight,ReportRadius,NearRadius}) +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 +end +do +CARGO_REPORTABLE={ +ClassName="CARGO_REPORTABLE" +} +function CARGO_REPORTABLE:New(CargoObject,Type,Name,Weight,ReportRadius) +local self=BASE:Inherit(self,CARGO:New(Type,Name,Weight)) +self:F({Type,Name,Weight,ReportRadius}) +self.CargoSet=SET_CARGO:New() +self.ReportRadius=ReportRadius or 1000 +self.CargoObject=CargoObject +return self +end +function CARGO_REPORTABLE:IsInRadius(PointVec2) +self:F({PointVec2}) +local Distance=0 +if self:IsLoaded()then +Distance=PointVec2:DistanceFromPointVec2(self.CargoCarrier:GetPointVec2()) +else +Distance=PointVec2:DistanceFromPointVec2(self.CargoObject:GetPointVec2()) +end +self:T(Distance) +if Distance<=self.ReportRadius then +return true +else +return false +end +end +function CARGO_REPORTABLE:MessageToGroup(Message,TaskGroup,Name) +local Prefix=Name and"@ "..Name..": "or"@ "..TaskGroup:GetCallsign()..": " +Message=Prefix..Message +MESSAGE:New(Message,20,"Cargo: "..self:GetName()):ToGroup(TaskGroup) +end +function CARGO_REPORTABLE:GetBoardingRange() +return self.ReportRadius +end +function CARGO_REPORTABLE:Respawn() +self:F({"Respawning"}) +for CargoID,CargoData in pairs(self.CargoSet:GetSet())do +local Cargo=CargoData +Cargo:Destroy() +Cargo:SetStartState("UnLoaded") +end +local CargoObject=self.CargoObject +CargoObject:Destroy() +local Template=CargoObject:GetTemplate() +CargoObject:Respawn(Template) +self:SetDeployed(false) +local WeightGroup=0 +self:SetStartState("UnLoaded") +end +end +do +CARGO_UNIT={ +ClassName="CARGO_UNIT" +} +function CARGO_UNIT:New(CargoUnit,Type,Name,Weight,NearRadius) +local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoUnit,Type,Name,Weight,NearRadius)) +self:F({Type,Name,Weight,NearRadius}) +self:T(CargoUnit) +self.CargoObject=CargoUnit +self:T(self.ClassName) +self:SetEventPriority(5) +return self +end +function CARGO_UNIT:Destroy() +self:F({CargoName=self:GetName()}) +_EVENTDISPATCHER:CreateEventDeleteCargo(self) +return self +end +function CARGO_UNIT:onenterUnBoarding(From,Event,To,ToPointVec2,NearRadius) +self:F({From,Event,To,ToPointVec2,NearRadius}) +NearRadius=NearRadius or 25 +local Angle=180 +local Speed=60 +local DeployDistance=9 +local RouteDistance=60 +if From=="Loaded"then +local CargoCarrier=self.CargoCarrier +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) +ToPointVec2=ToPointVec2 or CargoRoutePointVec2 +local DirectionVec3=CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2) +local Angle=CargoCarrierPointVec2:GetAngleDegrees(DirectionVec3) +local CargoDeployPointVec2=CargoCarrierPointVec2:Translate(DeployDistance,Angle) +local FromPointVec2=CargoCarrierPointVec2 +if self.CargoObject then +self.CargoObject:ReSpawn(CargoDeployPointVec2:GetVec3(),CargoDeployHeading) +self:F({"CargoUnits:",self.CargoObject:GetGroup():GetName()}) +self.CargoCarrier=nil +local Points={} +Points[#Points+1]=CargoCarrierPointVec2:WaypointGround(Speed) +Points[#Points+1]=ToPointVec2:WaypointGround(Speed) +local TaskRoute=self.CargoObject:TaskRoute(Points) +self.CargoObject:SetTask(TaskRoute,1) +self:__UnBoarding(1,ToPointVec2,NearRadius) +end +end +end +function CARGO_UNIT:onleaveUnBoarding(From,Event,To,ToPointVec2,NearRadius) +self:F({From,Event,To,ToPointVec2,NearRadius}) +NearRadius=NearRadius or 25 +local Angle=180 +local Speed=10 +local Distance=5 +if From=="UnBoarding"then +if self:IsNear(ToPointVec2,NearRadius)then +return true +else +self:__UnBoarding(1,ToPointVec2,NearRadius) +end +return false +end +end +function CARGO_UNIT:onafterUnBoarding(From,Event,To,ToPointVec2,NearRadius) +self:F({From,Event,To,ToPointVec2,NearRadius}) +NearRadius=NearRadius or 25 +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 CargoDeployPointVec2=StartPointVec2:Translate(Distance,CargoDeployHeading) +ToPointVec2=ToPointVec2 or POINT_VEC2:New(CargoDeployPointVec2:GetX(),CargoDeployPointVec2:GetY()) +if self.CargoObject then +self.CargoObject:ReSpawn(ToPointVec2:GetVec3(),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}) +local NearRadius=NearRadius or 25 +self.CargoInAir=self.CargoObject:InAir() +self:T(self.CargoInAir) +if not self.CargoInAir then +if self:IsNear(CargoCarrier:GetPointVec2(),NearRadius)then +self:Load(CargoCarrier,NearRadius,...) +else +local Speed=90 +local Angle=180 +local Distance=5 +NearRadius=NearRadius or 25 +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) +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(-1,CargoCarrier,NearRadius) +self.RunCount=0 +end +end +end +function CARGO_UNIT:onafterBoarding(From,Event,To,CargoCarrier,NearRadius,...) +self:F({From,Event,To,CargoCarrier.UnitName,NearRadius}) +if CargoCarrier and CargoCarrier:IsAlive()then +if CargoCarrier:InAir()==false then +if self:IsNear(CargoCarrier:GetPointVec2(),NearRadius)then +self:__Load(1,CargoCarrier,...) +else +self:__Boarding(-1,CargoCarrier,NearRadius,...) +self.RunCount=self.RunCount+1 +if self.RunCount>=20 then +self.RunCount=0 +local Speed=90 +local Angle=180 +local Distance=5 +NearRadius=NearRadius or 25 +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) +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,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:onenterBoarding(From,Event,To,CargoCarrier,NearRadius,...) +self:F({From,Event,To,CargoCarrier.UnitName,NearRadius}) +local Speed=90 +local Angle=180 +local Distance=5 +local NearRadius=NearRadius or 25 +if From=="UnLoaded"or From=="Boarding"then +end +end +function CARGO_UNIT:onenterLoaded(From,Event,To,CargoCarrier) +self:F({From,Event,To,CargoCarrier}) +self.CargoCarrier=CargoCarrier +if self.CargoObject then +self:T("Destroying") +self.CargoObject:Destroy() +end +end +end +do +CARGO_GROUP={ +ClassName="CARGO_GROUP", +} +function CARGO_GROUP:New(CargoGroup,Type,Name,ReportRadius) +local self=BASE:Inherit(self,CARGO_REPORTABLE:New(CargoGroup,Type,Name,0,ReportRadius)) +self:F({Type,Name,ReportRadius}) +self.CargoObject=CargoGroup +self:SetDeployed(false) +self.CargoGroup=CargoGroup +local WeightGroup=0 +for UnitID,UnitData in pairs(CargoGroup:GetUnits())do +local Unit=UnitData +local WeightUnit=Unit:GetDesc().massEmpty +WeightGroup=WeightGroup+WeightUnit +local CargoUnit=CARGO_UNIT:New(Unit,Type,Unit:GetName(),WeightUnit) +self.CargoSet:Add(CargoUnit:GetName(),CargoUnit) +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:OnEventCargoDead(EventData) +local Destroyed=false +if self:IsDestroyed()or self:IsUnLoaded()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:onenterBoarding(From,Event,To,CargoCarrier,NearRadius,...) +self:F({CargoCarrier.UnitName,From,Event,To}) +local NearRadius=NearRadius or 25 +if From=="UnLoaded"then +self.CargoSet:ForEach( +function(Cargo,...) +Cargo:__Board(1,CargoCarrier,NearRadius,...) +end,... +) +self:__Boarding(1,CargoCarrier,NearRadius,...) +end +end +function CARGO_GROUP:onenterLoaded(From,Event,To,CargoCarrier,...) +self:F({From,Event,To,CargoCarrier,...}) +if From=="UnLoaded"then +for CargoID,Cargo in pairs(self.CargoSet:GetSet())do +Cargo:Load(CargoCarrier) +end +end +self.CargoCarrier=CargoCarrier +end +function CARGO_GROUP:onafterBoarding(From,Event,To,CargoCarrier,NearRadius,...) +self:F({CargoCarrier.UnitName,From,Event,To}) +local NearRadius=NearRadius or 25 +local Boarded=true +local Cancelled=false +local Dead=true +self.CargoSet:Flush() +for CargoID,Cargo in pairs(self.CargoSet:GetSet())do +self:T({Cargo:GetName(),Cargo.current}) +if not Cargo:is("Loaded")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(1,CargoCarrier,NearRadius,...) +else +self:__Load(1,CargoCarrier,...) +end +else +self:__CancelBoarding(1,CargoCarrier,NearRadius,...) +end +else +self:__Destroyed(1,CargoCarrier,NearRadius,...) +end +end +function CARGO_GROUP:GetCount() +return self.CargoSet:Count() +end +function CARGO_GROUP:onenterUnBoarding(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) +Cargo:__UnBoard(Timer,ToPointVec2,NearRadius) +Timer=Timer+10 +end,{NearRadius} +) +self:__UnBoarding(1,ToPointVec2,NearRadius,...) +end +end +function CARGO_GROUP: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 +local UnBoarded=true +for CargoID,Cargo in pairs(self.CargoSet:GetSet())do +self:T(Cargo.current) +if not Cargo:is("UnLoaded")then +UnBoarded=false +end +end +if UnBoarded then +return true +else +self:__UnBoarding(1,ToPointVec2,NearRadius,...) +end +return false +end +end +function CARGO_GROUP:onafterUnBoarding(From,Event,To,ToPointVec2,NearRadius,...) +self:F({From,Event,To,ToPointVec2,NearRadius}) +self:__UnLoad(1,ToPointVec2,...) +end +function CARGO_GROUP:onenterUnLoaded(From,Event,To,ToPointVec2,...) +self:F({From,Event,To,ToPointVec2}) +if From=="Loaded"then +self.CargoSet:ForEach( +function(Cargo) +Cargo:UnLoad(ToPointVec2) +end +) +end +end +function CARGO_GROUP:RespawnOnDestroyed(RespawnDestroyed) +self:F({"In function RespawnOnDestroyed"}) +if RespawnDestroyed then +self.onenterDestroyed=function(self) +self:F("IN FUNCTION") +self:Respawn() +end +else +self.onenterDestroyed=nil +end +end +end +do +CARGO_PACKAGE={ +ClassName="CARGO_PACKAGE" +} +function CARGO_PACKAGE:New(CargoCarrier,Type,Name,Weight,ReportRadius,NearRadius) +local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoCarrier,Type,Name,Weight,ReportRadius,NearRadius)) +self:F({Type,Name,Weight,ReportRadius,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:GetPointVec2() +local Distance=CargoCarrierPoint:DistanceFromPointVec2(self.CargoCarrier:GetPointVec2()) +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 +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("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:E({"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:OnEventDead(EventData) +self:E({Dead=EventData.IniDCSUnitName,Target=self.Target}) +if self.Target then +if EventData.IniDCSUnitName==self.Target:GetName()then +self:E({"Target dead ",self.Target:GetName()}) +self:Destroyed() +self:LaseOff() +end +end +end +function SPOT:onafterLasing(From,Event,To) +if 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) +else +self:E({"Target is not alive",self.Target:IsAlive()}) +end +end +function SPOT:onafterLaseOff(From,Event,To) +self:E({"Stopped lasing for ",self.Target:GetName(),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 +OBJECT={ +ClassName="OBJECT", +ObjectName="", +} +function OBJECT:New(ObjectName,Test) +local self=BASE:Inherit(self,BASE:New()) +self:F2(ObjectName) +self.ObjectName=ObjectName +return self +end +function OBJECT:GetID() +self:F2(self.ObjectName) +local DCSObject=self:GetDCSObject() +if DCSObject then +local ObjectID=DCSObject:getID() +return ObjectID +end +return nil +end +function OBJECT:Destroy() +self:F2(self.ObjectName) +local DCSObject=self:GetDCSObject() +if DCSObject then +DCSObject:destroy() +end +return nil +end +IDENTIFIABLE={ +ClassName="IDENTIFIABLE", +IdentifiableName="", +} +local _CategoryName={ +[Unit.Category.AIRPLANE]="Airplane", +[Unit.Category.HELICOPTER]="Helicoper", +[Unit.Category.GROUND_UNIT]="Ground Identifiable", +[Unit.Category.SHIP]="Ship", +[Unit.Category.STRUCTURE]="Structure", +} +function IDENTIFIABLE:New(IdentifiableName) +local self=BASE:Inherit(self,OBJECT:New(IdentifiableName)) +self:F2(IdentifiableName) +self.IdentifiableName=IdentifiableName +return self +end +function IDENTIFIABLE:IsAlive() +self:F3(self.IdentifiableName) +local DCSIdentifiable=self:GetDCSObject() +if DCSIdentifiable then +local IdentifiableIsAlive=DCSIdentifiable:isExist() +return IdentifiableIsAlive +end +return false +end +function IDENTIFIABLE:GetName() +self:F2(self.IdentifiableName) +local IdentifiableName=self.IdentifiableName +return IdentifiableName +end +function IDENTIFIABLE:GetTypeName() +self:F2(self.IdentifiableName) +local DCSIdentifiable=self:GetDCSObject() +if DCSIdentifiable then +local IdentifiableTypeName=DCSIdentifiable:getTypeName() +self:T3(IdentifiableTypeName) +return IdentifiableTypeName +end +self:E(self.ClassName.." "..self.IdentifiableName.." not found!") +return nil +end +function IDENTIFIABLE:GetCategory() +self:F2(self.ObjectName) +local DCSObject=self:GetDCSObject() +if DCSObject then +local ObjectCategory=DCSObject:getCategory() +self:T3(ObjectCategory) +return ObjectCategory +end +return nil +end +function IDENTIFIABLE:GetCategoryName() +local DCSIdentifiable=self:GetDCSObject() +if DCSIdentifiable then +local IdentifiableCategoryName=_CategoryName[self:GetDesc().category] +return IdentifiableCategoryName +end +self:E(self.ClassName.." "..self.IdentifiableName.." not found!") +return nil +end +function IDENTIFIABLE:GetCoalition() +self:F2(self.IdentifiableName) +local DCSIdentifiable=self:GetDCSObject() +if DCSIdentifiable then +local IdentifiableCoalition=DCSIdentifiable:getCoalition() +self:T3(IdentifiableCoalition) +return IdentifiableCoalition +end +self:E(self.ClassName.." "..self.IdentifiableName.." not found!") +return nil +end +function IDENTIFIABLE:GetCountry() +self:F2(self.IdentifiableName) +local DCSIdentifiable=self:GetDCSObject() +if DCSIdentifiable then +local IdentifiableCountry=DCSIdentifiable:getCountry() +self:T3(IdentifiableCountry) +return IdentifiableCountry +end +self:E(self.ClassName.." "..self.IdentifiableName.." not found!") +return nil +end +function IDENTIFIABLE:GetDesc() +self:F2(self.IdentifiableName) +local DCSIdentifiable=self:GetDCSObject() +if DCSIdentifiable then +local IdentifiableDesc=DCSIdentifiable:getDesc() +self:T2(IdentifiableDesc) +return IdentifiableDesc +end +self:E(self.ClassName.." "..self.IdentifiableName.." not found!") +return nil +end +function IDENTIFIABLE:GetCallsign() +return'' +end +function IDENTIFIABLE:GetThreatLevel() +return 0,"Scenery" +end +POSITIONABLE={ +ClassName="POSITIONABLE", +PositionableName="", +} +POSITIONABLE.__={} +POSITIONABLE.__.Cargo={} +function POSITIONABLE:New(PositionableName) +local self=BASE:Inherit(self,IDENTIFIABLE:New(PositionableName)) +self.PositionableName=PositionableName +return self +end +function POSITIONABLE:GetPositionVec3() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionablePosition=DCSPositionable:getPosition().p +self:T3(PositionablePosition) +return PositionablePosition +end +return nil +end +function POSITIONABLE:GetVec2() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionableVec3=DCSPositionable:getPosition().p +local PositionableVec2={} +PositionableVec2.x=PositionableVec3.x +PositionableVec2.y=PositionableVec3.z +self:T2(PositionableVec2) +return PositionableVec2 +end +return nil +end +function POSITIONABLE:GetPointVec2() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionableVec3=DCSPositionable:getPosition().p +local PositionablePointVec2=POINT_VEC2:NewFromVec3(PositionableVec3) +self:T2(PositionablePointVec2) +return PositionablePointVec2 +end +return nil +end +function POSITIONABLE:GetPointVec3() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionableVec3=self:GetPositionVec3() +local PositionablePointVec3=POINT_VEC3:NewFromVec3(PositionableVec3) +self:T2(PositionablePointVec3) +return PositionablePointVec3 +end +return nil +end +function POSITIONABLE:GetCoordinate() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionableVec3=self:GetPositionVec3() +local PositionableCoordinate=COORDINATE:NewFromVec3(PositionableVec3) +PositionableCoordinate:SetHeading(self:GetHeading()) +PositionableCoordinate:SetVelocity(self:GetVelocityMPS()) +self:T2(PositionableCoordinate) +return PositionableCoordinate +end +return nil +end +function POSITIONABLE:GetRandomVec3(Radius) +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionablePointVec3=DCSPositionable:getPosition().p +if Radius then +local PositionableRandomVec3={} +local angle=math.random()*math.pi*2; +PositionableRandomVec3.x=PositionablePointVec3.x+math.cos(angle)*math.random()*Radius; +PositionableRandomVec3.y=PositionablePointVec3.y +PositionableRandomVec3.z=PositionablePointVec3.z+math.sin(angle)*math.random()*Radius; +self:T3(PositionableRandomVec3) +return PositionableRandomVec3 +else +self:E("Radius is nil, returning the PointVec3 of the POSITIONABLE",PositionablePointVec3) +return PositionablePointVec3 +end +end +return nil +end +function POSITIONABLE:GetVec3() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionableVec3=DCSPositionable:getPosition().p +self:T3(PositionableVec3) +return PositionableVec3 +end +return nil +end +function POSITIONABLE:GetBoundingBox() +self:F2() +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionableDesc=DCSPositionable:getDesc() +if PositionableDesc then +local PositionableBox=PositionableDesc.box +return PositionableBox +end +end +return nil +end +function POSITIONABLE:GetAltitude() +self:F2() +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionablePointVec3=DCSPositionable:getPoint() +return PositionablePointVec3.y +end +return nil +end +function POSITIONABLE:IsAboveRunway() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local Vec2=self:GetVec2() +local SurfaceType=land.getSurfaceType(Vec2) +local IsAboveRunway=SurfaceType==land.SurfaceType.RUNWAY +self:T2(IsAboveRunway) +return IsAboveRunway +end +return nil +end +function POSITIONABLE:GetHeading() +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionablePosition=DCSPositionable:getPosition() +if PositionablePosition then +local PositionableHeading=math.atan2(PositionablePosition.x.z,PositionablePosition.x.x) +if PositionableHeading<0 then +PositionableHeading=PositionableHeading+2*math.pi +end +PositionableHeading=PositionableHeading*180/math.pi +self:T2(PositionableHeading) +return PositionableHeading +end +end +return nil +end +function POSITIONABLE:InAir() +self:F2(self.PositionableName) +return nil +end +function POSITIONABLE:GetVelocity() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionableVelocityVec3=DCSPositionable:getVelocity() +self:T3(PositionableVelocityVec3) +return PositionableVelocityVec3 +end +return nil +end +function POSITIONABLE:GetHeight() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local PositionablePosition=DCSPositionable:getPosition() +if PositionablePosition then +local PositionableHeight=PositionablePosition.p.y +self:T2(PositionableHeight) +return PositionableHeight +end +end +return nil +end +function POSITIONABLE:GetVelocityKMH() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local VelocityVec3=self:GetVelocity() +local Velocity=(VelocityVec3.x^2+VelocityVec3.y^2+VelocityVec3.z^2)^0.5 +local Velocity=Velocity*3.6 +self:T3(Velocity) +return Velocity +end +return 0 +end +function POSITIONABLE:GetVelocityMPS() +self:F2(self.PositionableName) +local DCSPositionable=self:GetDCSObject() +if DCSPositionable then +local VelocityVec3=self:GetVelocity() +local Velocity=(VelocityVec3.x^2+VelocityVec3.y^2+VelocityVec3.z^2)^0.5 +self:T3(Velocity) +return Velocity +end +return 0 +end +function POSITIONABLE:GetMessageText(Message,Name) +local DCSObject=self:GetDCSObject() +if DCSObject then +Name=Name and(" ("..Name..")")or"" +local Callsign=string.format("[%s]",self:GetCallsign()~=""and self:GetCallsign()or self:GetName()) +local MessageText=Callsign..Name..": "..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) +self:F2({Message,Duration}) +local Name="" +local DCSObject=self:GetDCSObject() +if DCSObject then +if MessageCoalition==coalition.side.BLUE then +Name="Blue coalition" +end +if MessageCoalition==coalition.side.RED then +Name="Red coalition" +end +self:GetMessage(Message,Duration,Name):ToCoalition(MessageCoalition) +end +return nil +end +function POSITIONABLE:MessageTypeToCoalition(Message,MessageType,MessageCoalition) +self:F2({Message,MessageType}) +local Name="" +local DCSObject=self:GetDCSObject() +if DCSObject then +if MessageCoalition==coalition.side.BLUE then +Name="Blue coalition" +end +if MessageCoalition==coalition.side.RED then +Name="Red coalition" +end +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 +self:GetMessage(Message,Duration,Name):ToGroup(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:ForEachGroup( +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:E("bulding spot") +self.Spot=SPOT:New(self) +self.Spot:LaseOn(Target,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 +function POSITIONABLE:AddCargo(Cargo) +self.__.Cargo[Cargo]=Cargo +return self +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:CargoItemCount() +local ItemCount=0 +for CargoName,Cargo in pairs(self.__.Cargo)do +ItemCount=ItemCount+Cargo:GetCount() +end +return ItemCount +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 +CONTROLLABLE={ +ClassName="CONTROLLABLE", +ControllableName="", +WayPointFunctions={}, +} +function CONTROLLABLE:New(ControllableName) +local self=BASE:Inherit(self,POSITIONABLE:New(ControllableName)) +self:F2(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:GetUnits() +self:F2({self.ControllableName}) +local DCSControllable=self:GetDCSObject() +if DCSControllable then +local DCSUnits=DCSControllable:getUnits() +local Units={} +for Index,UnitData in pairs(DCSUnits)do +Units[#Units+1]=UNIT:Find(UnitData) +end +self:T3(Units) +return Units +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:GetFuel() +self:F(self.ControllableName) +return nil +end +function CONTROLLABLE:ClearTasks() +self:F2() +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 Controller=self:_GetController() +if WaitTime then +self.TaskScheduler:Schedule(Controller,Controller.pushTask,{DCSTask},WaitTime) +else +Controller:pushTask(DCSTask) +end +return self +end +return nil +end +function CONTROLLABLE:SetTask(DCSTask,WaitTime) +self:F2({DCSTask=DCSTask}) +local DCSControllable=self:GetDCSObject() +if DCSControllable then +local DCSControllableName=self:GetName() +local function SetTask(Controller,DCSTask) +if self and self:IsAlive()then +local Controller=self:_GetController() +Controller:setTask(DCSTask) +else +BASE:E(DCSControllableName.." is not alive anymore. Cannot set DCSTask "..DCSTask) +end +end +if not WaitTime or WaitTime==0 then +SetTask(self,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) +self:F2({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 +self:T3({DCSStopCondition}) +return DCSStopCondition +end +function CONTROLLABLE:TaskControlled(DCSTask,DCSStopCondition) +self:F2({DCSTask,DCSStopCondition}) +local DCSTaskControlled +DCSTaskControlled={ +id='ControlledTask', +params={ +task=DCSTask, +stopCondition=DCSStopCondition +} +} +self:T3({DCSTaskControlled}) +return DCSTaskControlled +end +function CONTROLLABLE:TaskCombo(DCSTasks) +self:F2({DCSTasks}) +local DCSTaskCombo +DCSTaskCombo={ +id='ComboTask', +params={ +tasks=DCSTasks +} +} +for TaskID,Task in ipairs(DCSTasks)do +self:T(Task) +end +self:T3({DCSTaskCombo}) +return DCSTaskCombo +end +function CONTROLLABLE:TaskWrappedAction(DCSCommand,Index) +self:F2({DCSCommand}) +local DCSTaskWrappedAction +DCSTaskWrappedAction={ +id="WrappedAction", +enabled=true, +number=Index or 1, +auto=false, +params={ +action=DCSCommand, +}, +} +self:T3({DCSTaskWrappedAction}) +return DCSTaskWrappedAction +end +function CONTROLLABLE:SetTaskWaypoint(Waypoint,Task) +Waypoint.task=self:TaskCombo({Task}) +self:T3({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:TaskAttackGroup(AttackGroup,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit) +self:F2({self.ControllableName,AttackGroup,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit}) +local DirectionEnabled=nil +if Direction then +DirectionEnabled=true +end +local AltitudeEnabled=nil +if Altitude then +AltitudeEnabled=true +end +local DCSTask +DCSTask={id='AttackGroup', +params={ +groupId=AttackGroup:GetID(), +weaponType=WeaponType, +expend=WeaponExpend, +attackQty=AttackQty, +directionEnabled=DirectionEnabled, +direction=Direction, +altitudeEnabled=AltitudeEnabled, +altitude=Altitude, +attackQtyLimit=AttackQtyLimit, +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskAttackUnit(AttackUnit,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,Visible,WeaponType) +self:F2({self.ControllableName,AttackUnit,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,Visible,WeaponType}) +local DCSTask +DCSTask={ +id='AttackUnit', +params={ +unitId=AttackUnit:GetID(), +groupAttack=GroupAttack or false, +visible=Visible or false, +expend=WeaponExpend or"Auto", +directionEnabled=Direction and true or false, +direction=Direction, +altitudeEnabled=Altitude and true or false, +altitude=Altitude or 30, +attackQtyLimit=AttackQty and true or false, +attackQty=AttackQty, +weaponType=WeaponType +} +} +self:T3(DCSTask) +return DCSTask +end +function CONTROLLABLE:TaskBombing(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType) +self:F2({self.ControllableName,Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType}) +local DCSTask +DCSTask={ +id='Bombing', +params={ +point=Vec2, +groupAttack=GroupAttack or false, +expend=WeaponExpend or"Auto", +attackQtyLimit=AttackQty and true or false, +attackQty=AttackQty, +directionEnabled=Direction and true or false, +direction=Direction, +altitudeEnabled=Altitude and true or false, +altitude=Altitude or 30, +weaponType=WeaponType, +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskAttackMapObject(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType) +self:F2({self.ControllableName,Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType}) +local DCSTask +DCSTask={ +id='AttackMapObject', +params={ +point=Vec2, +groupAttack=GroupAttack or false, +expend=WeaponExpend or"Auto", +attackQtyLimit=AttackQty and true or false, +attackQty=AttackQty, +directionEnabled=Direction and true or false, +direction=Direction, +altitudeEnabled=Altitude and true or false, +altitude=Altitude or 30, +weaponType=WeaponType, +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskOrbitCircleAtVec2(Point,Altitude,Speed) +self:F2({self.ControllableName,Point,Altitude,Speed}) +local LandHeight=land.getHeight(Point) +self:T3({LandHeight}) +local DCSTask={id='Orbit', +params={pattern=AI.Task.OrbitPattern.CIRCLE, +point=Point, +speed=Speed, +altitude=Altitude+LandHeight +} +} +return DCSTask +end +function CONTROLLABLE:TaskOrbitCircle(Altitude,Speed) +self:F2({self.ControllableName,Altitude,Speed}) +local DCSControllable=self:GetDCSObject() +if DCSControllable then +local ControllablePoint=self:GetVec2() +return self:TaskOrbitCircleAtVec2(ControllablePoint,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,ControllableAttack) +self:F2({self.ControllableName,Airbase,WeaponType,WeaponExpend,AttackQty,Direction,ControllableAttack}) +local DCSTask +DCSTask={id='BombingRunway', +params={ +point=Airbase:GetID(), +weaponType=WeaponType, +expend=WeaponExpend, +attackQty=AttackQty, +direction=Direction, +controllableAttack=ControllableAttack, +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskRefueling() +self:F2({self.ControllableName}) +local DCSTask +DCSTask={id='Refueling', +params={ +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskLandAtVec2(Point,Duration) +self:F2({self.ControllableName,Point,Duration}) +local DCSTask +if Duration and Duration>0 then +DCSTask={id='Land', +params={ +point=Point, +durationFlag=true, +duration=Duration, +}, +} +else +DCSTask={id='Land', +params={ +point=Point, +durationFlag=false, +}, +} +end +self:T3(DCSTask) +return DCSTask +end +function CONTROLLABLE:TaskLandAtZone(Zone,Duration,RandomPoint) +self:F2({self.ControllableName,Zone,Duration,RandomPoint}) +local Point +if RandomPoint then +Point=Zone:GetRandomVec2() +else +Point=Zone:GetVec2() +end +local DCSTask=self:TaskLandAtVec2(Point,Duration) +self:T3(DCSTask) +return DCSTask +end +function CONTROLLABLE:TaskFollow(FollowControllable,Vec3,LastWaypointIndex) +self:F2({self.ControllableName,FollowControllable,Vec3,LastWaypointIndex}) +local LastWaypointIndexFlag=false +if LastWaypointIndex then +LastWaypointIndexFlag=true +end +local DCSTask +DCSTask={ +id='Follow', +params={ +groupId=FollowControllable:GetID(), +pos=Vec3, +lastWptIndexFlag=LastWaypointIndexFlag, +lastWptIndex=LastWaypointIndex +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskEscort(FollowControllable,Vec3,LastWaypointIndex,EngagementDistance,TargetTypes) +self:F2({self.ControllableName,FollowControllable,Vec3,LastWaypointIndex,EngagementDistance,TargetTypes}) +local LastWaypointIndexFlag=false +if LastWaypointIndex then +LastWaypointIndexFlag=true +end +local DCSTask +DCSTask={id='Escort', +params={ +groupId=FollowControllable:GetID(), +pos=Vec3, +lastWptIndexFlag=LastWaypointIndexFlag, +lastWptIndex=LastWaypointIndex, +engagementDistMax=EngagementDistance, +targetTypes=TargetTypes, +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskFireAtPoint(Vec2,Radius,AmmoCount) +self:F2({self.ControllableName,Vec2,Radius,AmmoCount}) +local DCSTask +DCSTask={id='FireAtPoint', +params={ +point=Vec2, +radius=Radius, +expendQty=100, +expendQtyEnabled=false, +} +} +if AmmoCount then +DCSTask.params.expendQty=AmmoCount +DCSTask.params.expendQtyEnabled=true +end +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskHold() +self:F2({self.ControllableName}) +local DCSTask +DCSTask={id='Hold', +params={ +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskFAC_AttackGroup(AttackGroup,WeaponType,Designation,Datalink) +self:F2({self.ControllableName,AttackGroup,WeaponType,Designation,Datalink}) +local DCSTask +DCSTask={id='FAC_AttackGroup', +params={ +groupId=AttackGroup:GetID(), +weaponType=WeaponType, +designation=Designation, +datalink=Datalink, +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskEngageTargets(Distance,TargetTypes,Priority) +self:F2({self.ControllableName,Distance,TargetTypes,Priority}) +local DCSTask +DCSTask={id='EngageTargets', +params={ +maxDist=Distance, +targetTypes=TargetTypes, +priority=Priority +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskEngageTargetsInZone(Vec2,Radius,TargetTypes,Priority) +self:F2({self.ControllableName,Vec2,Radius,TargetTypes,Priority}) +local DCSTask +DCSTask={id='EngageTargetsInZone', +params={ +point=Vec2, +zoneRadius=Radius, +targetTypes=TargetTypes, +priority=Priority +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskEngageGroup(AttackGroup,Priority,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit) +self:F2({self.ControllableName,AttackGroup,Priority,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit}) +local DirectionEnabled=nil +if Direction then +DirectionEnabled=true +end +local AltitudeEnabled=nil +if Altitude then +AltitudeEnabled=true +end +local DCSTask +DCSTask={id='EngageControllable', +params={ +groupId=AttackGroup:GetID(), +weaponType=WeaponType, +expend=WeaponExpend, +attackQty=AttackQty, +directionEnabled=DirectionEnabled, +direction=Direction, +altitudeEnabled=AltitudeEnabled, +altitude=Altitude, +attackQtyLimit=AttackQtyLimit, +priority=Priority, +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskEngageUnit(EngageUnit,Priority,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,Visible,ControllableAttack) +self:F2({self.ControllableName,EngageUnit,Priority,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,Visible,ControllableAttack}) +local DCSTask +DCSTask={id='EngageUnit', +params={ +unitId=EngageUnit:GetID(), +priority=Priority or 1, +groupAttack=GroupAttack or false, +visible=Visible or false, +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, +controllableAttack=ControllableAttack, +}, +}, +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskAWACS() +self:F2({self.ControllableName}) +local DCSTask +DCSTask={id='AWACS', +params={ +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskTanker() +self:F2({self.ControllableName}) +local DCSTask +DCSTask={id='Tanker', +params={ +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskEWR() +self:F2({self.ControllableName}) +local DCSTask +DCSTask={id='EWR', +params={ +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskFAC_EngageGroup(AttackGroup,Priority,WeaponType,Designation,Datalink) +self:F2({self.ControllableName,AttackGroup,WeaponType,Priority,Designation,Datalink}) +local DCSTask +DCSTask={id='FAC_EngageControllable', +params={ +groupId=AttackGroup:GetID(), +weaponType=WeaponType, +designation=Designation, +datalink=Datalink, +priority=Priority, +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:EnRouteTaskFAC(Radius,Priority) +self:F2({self.ControllableName,Radius,Priority}) +local DCSTask +DCSTask={id='FAC', +params={ +radius=Radius, +priority=Priority +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskEmbarking(Point,Duration,EmbarkingControllable) +self:F2({self.ControllableName,Point,Duration,EmbarkingControllable.DCSControllable}) +local DCSTask +DCSTask={id='Embarking', +params={x=Point.x, +y=Point.y, +duration=Duration, +controllablesForEmbarking={EmbarkingControllable.ControllableID}, +durationFlag=true, +distributionFlag=false, +distribution={}, +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskEmbarkToTransport(Point,Radius) +self:F2({self.ControllableName,Point,Radius}) +local DCSTask +DCSTask={id='EmbarkToTransport', +params={x=Point.x, +y=Point.y, +zoneRadius=Radius, +} +} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:TaskFunction(FunctionString,...) +self:F2({FunctionString,arg}) +local DCSTask +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 +DCSTask=self:TaskWrappedAction( +self:CommandDoScript( +table.concat(DCSScript) +) +) +self:T(DCSTask) +return DCSTask +end +function CONTROLLABLE:TaskMission(TaskMission) +self:F2(Points) +local DCSTask +DCSTask={id='Mission',params={TaskMission,},} +self:T3({DCSTask}) +return DCSTask +end +do +function CONTROLLABLE:PatrolRoute() +local PatrolGroup=self +if not self:IsInstanceOf("GROUP")then +PatrolGroup=self:GetGroup() +end +self:E({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:E({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:E({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:E({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(0) +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) +if not type(ZoneList)=="table"then +ZoneList={ZoneList} +end +local PatrolGroup=self +if not self:IsInstanceOf("GROUP")then +PatrolGroup=self:GetGroup() +end +self:E({PatrolGroup=PatrolGroup:GetName()}) +if PatrolGroup:IsGround()or PatrolGroup:IsShip()then +local Waypoints=PatrolGroup:GetTemplateRoutePoints() +local Waypoint=Waypoints[math.random(1,#Waypoints)] +local FromCoord=PatrolGroup:GetCoordinate() +local RandomZone=ZoneList[math.random(1,#ZoneList)] +local ToCoord=RandomZone:GetRandomCoordinate(10) +local Route={} +Route[#Route+1]=FromCoord:WaypointGround(120) +Route[#Route+1]=ToCoord:WaypointGround(Speed,Formation) +local TaskRouteToZone=PatrolGroup:TaskFunction("CONTROLLABLE.PatrolZones",ZoneList,Speed,Formation) +PatrolGroup:SetTaskWaypoint(Route[#Route],TaskRouteToZone) +PatrolGroup:Route(Route,1) +end +end +end +function CONTROLLABLE:TaskRoute(Points) +self:F2(Points) +local DCSTask +DCSTask={id='Mission',params={route={points=Points,},},} +self:T3({DCSTask}) +return DCSTask +end +function CONTROLLABLE:RouteToVec2(Point,Speed) +self:F2({Point,Speed}) +local ControllablePoint=self:GetUnit(1):GetVec2() +local PointFrom={} +PointFrom.x=ControllablePoint.x +PointFrom.y=ControllablePoint.y +PointFrom.type="Turning Point" +PointFrom.action="Turning Point" +PointFrom.speed=Speed +PointFrom.speed_locked=true +PointFrom.properties={ +["vnav"]=1, +["scale"]=0, +["angle"]=0, +["vangle"]=0, +["steer"]=2, +} +local PointTo={} +PointTo.x=Point.x +PointTo.y=Point.y +PointTo.type="Turning Point" +PointTo.action="Fly Over Point" +PointTo.speed=Speed +PointTo.speed_locked=true +PointTo.properties={ +["vnav"]=1, +["scale"]=0, +["angle"]=0, +["vangle"]=0, +["steer"]=2, +} +local Points={PointFrom,PointTo} +self:T3(Points) +self:Route(Points) +return self +end +function CONTROLLABLE:RouteToVec3(Point,Speed) +self:F2({Point,Speed}) +local ControllableVec3=self:GetUnit(1):GetVec3() +local PointFrom={} +PointFrom.x=ControllableVec3.x +PointFrom.y=ControllableVec3.z +PointFrom.alt=ControllableVec3.y +PointFrom.alt_type="BARO" +PointFrom.type="Turning Point" +PointFrom.action="Turning Point" +PointFrom.speed=Speed +PointFrom.speed_locked=true +PointFrom.properties={ +["vnav"]=1, +["scale"]=0, +["angle"]=0, +["vangle"]=0, +["steer"]=2, +} +local PointTo={} +PointTo.x=Point.x +PointTo.y=Point.z +PointTo.alt=Point.y +PointTo.alt_type="BARO" +PointTo.type="Turning Point" +PointTo.action="Fly Over Point" +PointTo.speed=Speed +PointTo.speed_locked=true +PointTo.properties={ +["vnav"]=1, +["scale"]=0, +["angle"]=0, +["vangle"]=0, +["steer"]=2, +} +local Points={PointFrom,PointTo} +self:T3(Points) +self:Route(Points) +return self +end +function CONTROLLABLE:Route(Route,DelaySeconds) +self:F2(Route) +local DCSControllable=self:GetDCSObject() +if DCSControllable then +local RouteTask=self:TaskRoute(Route) +self:SetTask(RouteTask,DelaySeconds or 1) +return self +end +return nil +end +function CONTROLLABLE:RouteGroundTo(ToCoordinate,Speed,Formation,DelaySeconds) +local FromCoordinate=self:GetCoordinate() +local FromWP=FromCoordinate:WaypointGround() +local ToWP=ToCoordinate:WaypointGround(Speed,Formation) +self:Route({FromWP,ToWP},DelaySeconds) +return self +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/1.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/1.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/1.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=60/3.6 +end +local Points={PointFrom,PointTo} +self:T3(Points) +self:Route(Points) +return self +end +return nil +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 +self:T({DetectionVisual,DetectionOptical,DetectionRadar,DetectionIRST,DetectionRWR,DetectionDLINK}) +return self:_GetController():getDetectedTargets(DetectionVisual,DetectionOptical,DetectionRadar,DetectionIRST,DetectionRWR,DetectionDLINK) +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: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: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: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:OptionRTBBingoFuel(RTB) +self:F2({self.ControllableName}) +RTB=RTB or true +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.GROUND.id.RTB_ON_OUT_OF_AMMO,WeaponsFlag) +end +return self +end +return nil +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:GetSize() +local DCSObject=self:GetDCSObject() +if DCSObject then +return 1 +else +return 0 +end +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"} +} +function GROUP:Register(GroupName) +self=BASE:Inherit(self,CONTROLLABLE:New(GroupName)) +self:F2(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 PositionablePosition=DCSPositionable:getUnits()[1]:getPosition().p +self:T3(PositionablePosition) +return PositionablePosition +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:Destroy() +self:F2(self.GroupName) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +for Index,UnitData in pairs(DCSGroup:getUnits())do +self:CreateEventCrash(timer.getTime(),UnitData) +end +DCSGroup:destroy() +DCSGroup=nil +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", +} +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:GetUnit(UnitNumber) +self:F2({self.GroupName,UnitNumber}) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local DCSUnit=DCSGroup:getUnit(UnitNumber) +local UnitFound=UNIT:Find(DCSGroup:getUnit(UnitNumber)) +self:T2(UnitFound) +return UnitFound +end +return nil +end +function GROUP:GetDCSUnit(UnitNumber) +self:F2({self.GroupName,UnitNumber}) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local DCSUnitFound=DCSGroup:getUnit(UnitNumber) +self:T3(DCSUnitFound) +return DCSUnitFound +end +return nil +end +function GROUP:GetSize() +self:F2({self.GroupName}) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local GroupSize=DCSGroup:getSize() +if GroupSize then +self:T3(GroupSize) +return GroupSize +else +return 0 +end +end +return nil +end +function GROUP:GetInitialSize() +self:F2({self.GroupName}) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local GroupInitialSize=DCSGroup:getInitialSize() +self:T3(GroupInitialSize) +return GroupInitialSize +end +return nil +end +function GROUP:GetDCSUnits() +self:F2({self.GroupName}) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local DCSUnits=DCSGroup:getUnits() +self:T3(DCSUnits) +return DCSUnits +end +return nil +end +function GROUP:Activate() +self:F2({self.GroupName}) +trigger.action.activateGroup(self:GetDCSObject()) +return self:GetDCSObject() +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 +return nil +end +function GROUP:GetVec2() +self:F2(self.GroupName) +local UnitPoint=self:GetUnit(1) +UnitPoint:GetVec2() +local GroupPointVec2=UnitPoint:GetVec2() +self:T3(GroupPointVec2) +return GroupPointVec2 +end +function GROUP:GetVec3() +self:F2(self.GroupName) +local GroupVec3=self:GetUnit(1):GetVec3() +self:T3(GroupVec3) +return GroupVec3 +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 +return nil +end +function GROUP:GetCoordinate() +self:F2(self.PositionableName) +local FirstUnit=self:GetUnit(1) +if FirstUnit then +local FirstUnitCoordinate=FirstUnit:GetCoordinate() +self:T3(FirstUnitCoordinate) +return FirstUnitCoordinate +end +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 +return nil +end +function GROUP:GetHeading() +self:F2(self.GroupName) +local GroupSize=self:GetSize() +local HeadingAccumulator=0 +if GroupSize then +for i=1,GroupSize do +HeadingAccumulator=HeadingAccumulator+self:GetUnit(i):GetHeading() +end +return math.floor(HeadingAccumulator/GroupSize) +end +return nil +end +function GROUP:GetFuel() +self:F(self.ControllableName) +local DCSControllable=self:GetDCSObject() +if DCSControllable then +local GroupSize=self:GetSize() +local TotalFuel=0 +for UnitID,UnitData in pairs(self:GetUnits())do +local Unit=UnitData +local UnitFuel=Unit:GetFuel() +self:F({Fuel=UnitFuel}) +TotalFuel=TotalFuel+UnitFuel +end +local GroupFuel=TotalFuel/GroupSize +return GroupFuel +end +return 0 +end +do +function GROUP:IsCompletelyInZone(Zone) +self:F2({self.GroupName,Zone}) +if not self:IsAlive()then return false end +for UnitID,UnitData in pairs(self:GetUnits())do +local Unit=UnitData +if Zone:IsVec3InZone(Unit:GetVec3())then +else +return false +end +end +return true +end +function GROUP:IsPartlyInZone(Zone) +self:F2({self.GroupName,Zone}) +local IsOneUnitInZone=false +local IsOneUnitOutsideZone=false +if not self:IsAlive()then return false end +for UnitID,UnitData in pairs(self:GetUnits())do +local Unit=UnitData +if Zone:IsVec3InZone(Unit:GetVec3())then +IsOneUnitInZone=true +else +IsOneUnitOutsideZone=true +end +end +if IsOneUnitInZone and IsOneUnitOutsideZone then +return true +else +return false +end +end +function GROUP:IsNotInZone(Zone) +self:F2({self.GroupName,Zone}) +if not self:IsAlive()then return true end +for UnitID,UnitData in pairs(self:GetUnits())do +local Unit=UnitData +if Zone:IsVec3InZone(Unit:GetVec3())then +return false +end +end +return true +end +function GROUP:CountInZone(Zone) +self:F2({self.GroupName,Zone}) +local Count=0 +if not self:IsAlive()then return Count end +for UnitID,UnitData in pairs(self:GetUnits())do +local Unit=UnitData +if Zone:IsVec3InZone(Unit:GetVec3())then +Count=Count+1 +end +end +return Count +end +function GROUP:IsAir() +self:F2(self.GroupName) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local IsAirResult=DCSGroup:getCategory()==Group.Category.AIRPLANE or DCSGroup:getCategory()==Group.Category.HELICOPTER +self:T3(IsAirResult) +return IsAirResult +end +return nil +end +function GROUP:IsHelicopter() +self:F2(self.GroupName) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local GroupCategory=DCSGroup:getCategory() +self:T2(GroupCategory) +return GroupCategory==Group.Category.HELICOPTER +end +return nil +end +function GROUP:IsAirPlane() +self:F2() +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local GroupCategory=DCSGroup:getCategory() +self:T2(GroupCategory) +return GroupCategory==Group.Category.AIRPLANE +end +return nil +end +function GROUP:IsGround() +self:F2() +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local GroupCategory=DCSGroup:getCategory() +self:T2(GroupCategory) +return GroupCategory==Group.Category.GROUND +end +return nil +end +function GROUP:IsShip() +self:F2() +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local GroupCategory=DCSGroup:getCategory() +self:T2(GroupCategory) +return GroupCategory==Group.Category.SHIP +end +return nil +end +function GROUP:AllOnGround() +self:F2() +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local AllOnGroundResult=true +for Index,UnitData in pairs(DCSGroup:getUnits())do +if UnitData:inAir()then +AllOnGroundResult=false +end +end +self:T3(AllOnGroundResult) +return AllOnGroundResult +end +return nil +end +end +do +function GROUP:SetAIOnOff(AIOnOff) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local DCSController=DCSGroup:getController() +if DCSController then +DCSController:setOnOff(AIOnOff) +return self +end +end +return nil +end +function GROUP:SetAIOn() +return self:SetAIOnOff(true) +end +function GROUP:SetAIOff() +return self:SetAIOnOff(false) +end +end +function GROUP:GetMaxVelocity() +self:F2() +local DCSGroup=self:GetDCSObject() +if DCSGroup then +local GroupVelocityMax=0 +for Index,UnitData in pairs(DCSGroup:getUnits())do +local UnitVelocityVec3=UnitData:getVelocity() +local UnitVelocity=math.abs(UnitVelocityVec3.x)+math.abs(UnitVelocityVec3.y)+math.abs(UnitVelocityVec3.z) +if UnitVelocity>GroupVelocityMax then +GroupVelocityMax=UnitVelocity +end +end +return GroupVelocityMax +end +return nil +end +function GROUP:GetMinHeight() +self:F2() +end +function GROUP:GetMaxHeight() +self:F2() +end +function GROUP:Respawn(Template) +if self:IsAlive()then +local Vec3=self:GetVec3() +Template.x=Vec3.x +Template.y=Vec3.z +self:E(#Template.units) +for UnitID,UnitData in pairs(self:GetUnits())do +local GroupUnit=UnitData +self:E(GroupUnit:GetName()) +if GroupUnit:IsAlive()then +local GroupUnitVec3=GroupUnit:GetVec3() +local GroupUnitHeading=GroupUnit:GetHeading() +Template.units[UnitID].alt=GroupUnitVec3.y +Template.units[UnitID].x=GroupUnitVec3.x +Template.units[UnitID].y=GroupUnitVec3.z +Template.units[UnitID].heading=GroupUnitHeading +self:E({UnitID,Template.units[UnitID],Template.units[UnitID]}) +end +end +end +self:Destroy() +_DATABASE:Spawn(Template) +self:ResetEvents() +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: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: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 +do +function GROUP:RouteRTB(RTBAirbase,Speed) +self:F2({RTBAirbase,Speed}) +local DCSGroup=self:GetDCSObject() +if DCSGroup then +if RTBAirbase then +local GroupPoint=self:GetVec2() +local GroupVelocity=self:GetUnit(1):GetDesc().speedMax +local PointFrom={} +PointFrom.x=GroupPoint.x +PointFrom.y=GroupPoint.y +PointFrom.type="Turning Point" +PointFrom.action="Turning Point" +PointFrom.speed=GroupVelocity +local PointTo={} +local AirbasePointVec2=RTBAirbase:GetPointVec2() +local AirbaseAirPoint=AirbasePointVec2:WaypointAir( +POINT_VEC3.RoutePointAltType.BARO, +"Land", +"Landing", +Speed or self:GetUnit(1):GetDesc().speedMax +) +AirbaseAirPoint["airdromeId"]=RTBAirbase:GetID() +AirbaseAirPoint["speed_locked"]=true, +self:E(AirbaseAirPoint) +local Points={PointFrom,AirbaseAirPoint} +self:T3(Points) +local Template=self:GetTemplate() +Template.route.points=Points +self:Respawn(Template) +self:Route(Points) +self:Respawn(Template) +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 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) +end +end +self:F2(PlayerNames) +return PlayerNames +end +end +UNIT={ +ClassName="UNIT", +} +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) +local UnitName=DCSUnit:getName() +local UnitFound=_DATABASE:FindUnit(UnitName) +return UnitFound +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:ReSpawn(SpawnVec3,Heading) +local SpawnGroupTemplate=UTILS.DeepCopy(_DATABASE:GetGroupTemplateFromUnitName(self:Name())) +self:T(SpawnGroupTemplate) +local SpawnGroup=self:GetGroup() +if SpawnGroup then +local Vec3=SpawnGroup:GetVec3() +SpawnGroupTemplate.x=SpawnVec3.x +SpawnGroupTemplate.y=SpawnVec3.z +self:E(#SpawnGroupTemplate.units) +for UnitID,UnitData in pairs(SpawnGroup:GetUnits())do +local GroupUnit=UnitData +self:E(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:E({UnitID,SpawnGroupTemplate.units[UnitID],SpawnGroupTemplate.units[UnitID]}) +end +end +end +for UnitTemplateID,UnitTemplateData in pairs(SpawnGroupTemplate.units)do +self:T(UnitTemplateData.name) +if UnitTemplateData.name==self:Name()then +self:T("Adjusting") +SpawnGroupTemplate.units[UnitTemplateID].alt=SpawnVec3.y +SpawnGroupTemplate.units[UnitTemplateID].x=SpawnVec3.x +SpawnGroupTemplate.units[UnitTemplateID].y=SpawnVec3.z +SpawnGroupTemplate.units[UnitTemplateID].heading=Heading +self:E({UnitTemplateID,SpawnGroupTemplate.units[UnitTemplateID],SpawnGroupTemplate.units[UnitTemplateID]}) +else +self:E(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 +_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() +return UnitCallSign +end +self:E(self.ClassName.." "..self.UnitName.." not found!") +return nil +end +function UNIT:GetPlayerName() +self:F2(self.UnitName) +local DCSUnit=self:GetDCSObject() +if DCSUnit then +local PlayerName=DCSUnit:getPlayerName() +if PlayerName==nil then +PlayerName="" +end +return PlayerName +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:GetGroup() +self:F2(self.UnitName) +local DCSUnit=self:GetDCSObject() +if DCSUnit then +local UnitGroup=GROUP:Find(DCSUnit:getGroup()) +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: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:F(self.UnitName) +local DCSUnit=self:GetDCSObject() +if DCSUnit then +local UnitFuel=DCSUnit:getFuel() +return UnitFuel +end +return nil +end +function UNIT:GetUnits() +self:F2({self.UnitName}) +local DCSUnit=self:GetDCSObject() +if DCSUnit then +local DCSUnits=DCSUnit:getUnits() +local Units={} +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: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 +self:T(Attributes) +if self:IsGround()then +self:T("Ground") +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 +self:T("Air") +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 +self:T("Ship") +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 +self:T2(ThreatLevel) +return ThreatLevel,ThreatText +end +function UNIT:IsInZone(Zone) +self:F2({self.UnitName,Zone}) +if self:IsAlive()then +local IsInZone=Zone:IsVec3InZone(self:GetVec3()) +self:T2({IsInZone}) +return IsInZone +end +return false +end +function UNIT:IsNotInZone(Zone) +self:F2({self.UnitName,Zone}) +if self:IsAlive()then +local IsInZone=not Zone:IsVec3InZone(self:GetVec3()) +self:T({IsInZone}) +return IsInZone +else +return false +end +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:IsAir() +self:F2() +local DCSUnit=self:GetDCSObject() +if DCSUnit then +local UnitDescriptor=DCSUnit:getDesc() +self:T3({UnitDescriptor.category,Unit.Category.AIRPLANE,Unit.Category.HELICOPTER}) +local IsAirResult=(UnitDescriptor.category==Unit.Category.AIRPLANE)or(UnitDescriptor.category==Unit.Category.HELICOPTER) +self:T3(IsAirResult) +return IsAirResult +end +return nil +end +function UNIT:IsGround() +self:F2() +local DCSUnit=self:GetDCSObject() +if DCSUnit then +local UnitDescriptor=DCSUnit:getDesc() +self:T3({UnitDescriptor.category,Unit.Category.GROUND_UNIT}) +local IsGroundResult=(UnitDescriptor.category==Unit.Category.GROUND_UNIT) +self:T3(IsGroundResult) +return IsGroundResult +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:E(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() +self:F2(self.UnitName) +local DCSUnit=self:GetDCSObject() +if DCSUnit then +local UnitInAir=DCSUnit:inAir() +self:T3(UnitInAir) +return UnitInAir +end +return nil +end +do +function UNIT:HandleEvent(Event,EventFunction) +self:EventDispatcher():OnEventForUnit(self:GetName(),EventFunction,self,Event) +return self +end +function UNIT:UnHandleEvent(Event) +self:EventDispatcher():RemoveForUnit(self:GetName(),self,Event) +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 +end +CLIENT={ +ONBOARDSIDE={ +NONE=0, +LEFT=1, +RIGHT=2, +BACK=3, +FRONT=4 +}, +ClassName="CLIENT", +ClientName=nil, +ClientAlive=false, +ClientTransport=false, +ClientBriefingShown=false, +_Menus={}, +_Tasks={}, +Messages={ +} +} +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:F(ClientName) +self.ClientName=ClientName +self.MessageSwitch=true +self.ClientAlive2=false +self.AliveCheckScheduler=SCHEDULER:New(self,self._AliveCheckScheduler,{"Client Alive "..ClientName},1,5) +self:E(self) +return self +end +function CLIENT:Transport() +self:F() +self.ClientTransport=true +return self +end +function CLIENT:AddBriefing(ClientBriefing) +self:F(ClientBriefing) +self.ClientBriefing=ClientBriefing +self.ClientBriefingShown=false +return self +end +function CLIENT:ShowBriefing() +self:F({self.ClientName,self.ClientBriefingShown}) +if not self.ClientBriefingShown then +self.ClientBriefingShown=true +local Briefing="" +if self.ClientBriefing then +Briefing=Briefing..self.ClientBriefing +end +Briefing=Briefing.." Press [LEFT ALT]+[B] to view the complete mission briefing." +self:Message(Briefing,60,"Briefing") +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 +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.ClientGroupUnit=nil +return nil +end +function CLIENT:GetClientGroupID() +local ClientGroup=self:GetDCSGroup() +return self.ClientGroupID +end +function CLIENT:GetClientGroupName() +local ClientGroup=self:GetDCSGroup() +self:T(self.ClientGroupName) +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) +self:T2(ClientUnit) +return ClientUnit +end +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.SwitchMessages(PrmTable) +PrmTable[1].MessageSwitch=PrmTable[2] +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:FindByName(StaticName,RaiseError) +local StaticFound=_DATABASE:FindStatic(StaticName) +self.StaticName=StaticName +if StaticFound then +StaticFound:F3({StaticName}) +return StaticFound +end +if RaiseError==nil or RaiseError==true then +error("STATIC not found for: "..StaticName) +end +return nil +end +function STATIC:Register(StaticName) +local self=BASE:Inherit(self,POSITIONABLE:New(StaticName)) +self.StaticName=StaticName +return self +end +function STATIC:GetDCSObject() +local DCSStatic=StaticObject.getByName(self.StaticName) +if DCSStatic then +return DCSStatic +end +return nil +end +function STATIC:GetThreatLevel() +return 1,"Static" +end +AIRBASE={ +ClassName="AIRBASE", +CategoryName={ +[Airbase.Category.AIRDROME]="Airdrome", +[Airbase.Category.HELIPAD]="Helipad", +[Airbase.Category.SHIP]="Ship", +}, +} +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", +["Mellan_Airstrip"]="Mellan Airstrip", +["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"]="Ford", +} +function AIRBASE:Register(AirbaseName) +local self=BASE:Inherit(self,POSITIONABLE:New(AirbaseName)) +self.AirbaseName=AirbaseName +self.AirbaseZone=ZONE_RADIUS:New(AirbaseName,self:GetVec2(),8000) +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:GetDCSObject() +local DCSAirbase=Airbase.getByName(self.AirbaseName) +if DCSAirbase then +return DCSAirbase +end +return nil +end +function AIRBASE:GetZone() +return self.AirbaseZone +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 +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(true) +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.PlayerEnterUnit) +self:HandleEvent(EVENTS.PlayerLeaveUnit) +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:_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 +if self.Players[PlayerName].Penalty>self.Fratricide*0.50 then +if self.Players[PlayerName].PenaltyWarning<1 then +MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than "..self.Fratricide..", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: "..self.Players[PlayerName].Penalty, +MESSAGE.Type.Information +):ToAll() +self.Players[PlayerName].PenaltyWarning=self.Players[PlayerName].PenaltyWarning+1 +end +end +if self.Players[PlayerName].Penalty>self.Fratricide then +MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", +MESSAGE.Type.Information +):ToAll() +UnitData:GetGroup():Destroy() +end +end +end +function SCORING:AddGoalScore(PlayerUnit,GoalTag,Text,Score) +local PlayerName=PlayerUnit:GetPlayerName() +self:E({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:E({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..MissionName.." : "..Text.." Score: "..Score,MESSAGE.Type.Information):ToAll() +self:ScoreCSV(PlayerName,"","TASK_"..MissionName:gsub(' ','_'),1,Score,PlayerUnit:GetName()) +end +end +function SCORING:_AddMissionScore(Mission,Text,Score) +local MissionName=Mission:GetName() +self:E({Mission,Text,Score}) +self:E(self.Players) +for PlayerName,PlayerData in pairs(self.Players)do +self:E(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 '"..MissionName.."'. ".. +Score.." mission score!", +MESSAGE.Type.Information):ToAll() +self:ScoreCSV(PlayerName,"","MISSION_"..MissionName:gsub(' ','_'),1,Score) +end +end +end +function SCORING:OnEventPlayerEnterUnit(Event) +if Event.IniUnit then +self:_AddPlayerFromUnit(Event.IniUnit) +local Menu=MENU_GROUP:New(Event.IniGroup,'Scoring') +local ReportGroupSummary=MENU_GROUP_COMMAND:New(Event.IniGroup,'Summary report players in group',Menu,SCORING.ReportScoreGroupSummary,self,Event.IniGroup) +local ReportGroupDetailed=MENU_GROUP_COMMAND:New(Event.IniGroup,'Detailed report players in group',Menu,SCORING.ReportScoreGroupDetailed,self,Event.IniGroup) +local ReportToAllSummary=MENU_GROUP_COMMAND:New(Event.IniGroup,'Summary report all players',Menu,SCORING.ReportScoreAllSummary,self,Event.IniGroup) +self:SetState(Event.IniUnit,"ScoringMenu",Menu) +end +end +function SCORING:OnEventPlayerLeaveUnit(Event) +if Event.IniUnit then +local Menu=self:GetState(Event.IniUnit,"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:E({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:E({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:E({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:E({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:E({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:E({ReportHits,ScoreHits,PenaltyHits}) +local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName) +ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys +self:E({ReportDestroys,ScoreDestroys,PenaltyDestroys}) +local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName) +ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges +self:E({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges}) +local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName) +ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals +self:E({ReportGoals,ScoreGoals,PenaltyGoals}) +local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName) +ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions +self:E({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.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:E({ReportHits,ScoreHits,PenaltyHits}) +local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName) +ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys +self:E({ReportDestroys,ScoreDestroys,PenaltyDestroys}) +local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName) +ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges +self:E({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges}) +local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName) +ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals +self:E({ReportGoals,ScoreGoals,PenaltyGoals}) +local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName) +ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions +self:E({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("Report Score All Players") +for PlayerName,PlayerData in pairs(self.Players)do +if PlayerName then +local ReportHits,ScoreHits,PenaltyHits=self:ReportDetailedPlayerHits(PlayerName) +ReportHits=ReportHits~=""and"\n- "..ReportHits or ReportHits +self:E({ReportHits,ScoreHits,PenaltyHits}) +local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName) +ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys +self:E({ReportDestroys,ScoreDestroys,PenaltyDestroys}) +local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName) +ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges +self:E({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges}) +local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName) +ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals +self:E({ReportGoals,ScoreGoals,PenaltyGoals}) +local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName) +ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions +self:E({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:E("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) +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}) +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 +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()=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) +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}) +return self:SpawnWithIndex(self.SpawnIndex+1) +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:SpawnWithIndex(SpawnIndex) +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 +if SpawnTemplate.CategoryID==Group.Category.HELICOPTER or SpawnTemplate.CategoryID==Group.Category.AIRPLANE then +if SpawnTemplate.route.points[1].type=="TakeOffParking"then +SpawnTemplate.uncontrolled=self.SpawnUnControlled +end +end +end +self:HandleEvent(EVENTS.Birth,self._OnBirth) +self:HandleEvent(EVENTS.Dead,self._OnDeadOrCrash) +self:HandleEvent(EVENTS.Crash,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=SCHEDULER:New() +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) +self:E({self.SpawnTemplatePrefix,SpawnAirbase,Takeoff,TakeoffAltitude}) +local PointVec3=SpawnAirbase:GetPointVec3() +self:T2(PointVec3) +Takeoff=Takeoff or SPAWN.Takeoff.Hot +if self:_GetSpawnIndex(self.SpawnIndex+1)then +local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate +if SpawnTemplate then +self:T({"Current point of ",self.SpawnTemplatePrefix,SpawnAirbase}) +local SpawnPoint=SpawnTemplate.route.points[1] +SpawnPoint.linkUnit=nil +SpawnPoint.helipadId=nil +SpawnPoint.airdromeId=nil +local AirbaseID=SpawnAirbase:GetID() +local AirbaseCategory=SpawnAirbase:GetDesc().category +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] +for UnitID=1,#SpawnTemplate.units do +self:T('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) +local UnitTemplate=SpawnTemplate.units[UnitID] +UnitTemplate.parking=nil +UnitTemplate.parking_id=nil +UnitTemplate.alt=0 +local SX=UnitTemplate.x +local SY=UnitTemplate.y +local BX=SpawnPoint.x +local BY=SpawnPoint.y +local TX=PointVec3.x+(SX-BX) +local TY=PointVec3.z+(SY-BY) +UnitTemplate.x=TX +UnitTemplate.y=TY +if Takeoff==GROUP.Takeoff.Air then +UnitTemplate.alt=PointVec3.y+(TakeoffAltitude or 200) +end +self:T('After Translation SpawnTemplate.units['..UnitID..'].x = '..UnitTemplate.x..', SpawnTemplate.units['..UnitID..'].y = '..UnitTemplate.y) +end +SpawnPoint.x=PointVec3.x +SpawnPoint.y=PointVec3.z +if Takeoff==GROUP.Takeoff.Air then +SpawnPoint.alt=PointVec3.y+(TakeoffAltitude or 200) +end +SpawnTemplate.x=PointVec3.x +SpawnTemplate.y=PointVec3.z +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()},1) +end +end +return GroupSpawned +end +end +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}) +for UnitID=1,#SpawnTemplate.units do +self:T('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=Vec3.x+(SX-BX) +local TY=Vec3.z+(SY-BY) +SpawnTemplate.units[UnitID].x=TX +SpawnTemplate.units[UnitID].y=TY +SpawnTemplate.units[UnitID].alt=Vec3.y +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 +SpawnTemplate.route.points[1].alt=Vec3.y +SpawnTemplate.x=Vec3.x +SpawnTemplate.y=Vec3.z +return self:SpawnWithIndex(self.SpawnIndex) +end +end +return nil +end +function SPAWN:SpawnFromVec2(Vec2,SpawnIndex) +self:F({self.SpawnTemplatePrefix,Vec2,SpawnIndex}) +local PointVec2=POINT_VEC2:NewFromVec2(Vec2) +return self:SpawnFromVec3(PointVec2:GetVec3(),SpawnIndex) +end +function SPAWN:SpawnFromUnit(HostUnit,SpawnIndex) +self:F({self.SpawnTemplatePrefix,HostUnit,SpawnIndex}) +if HostUnit and HostUnit:IsAlive()~=nil then +return self:SpawnFromVec3(HostUnit:GetVec3(),SpawnIndex) +end +return nil +end +function SPAWN:SpawnFromStatic(HostStatic,SpawnIndex) +self:F({self.SpawnTemplatePrefix,HostStatic,SpawnIndex}) +if HostStatic and HostStatic:IsAlive()then +return self:SpawnFromVec3(HostStatic:GetVec3(),SpawnIndex) +end +return nil +end +function SPAWN:SpawnInZone(Zone,RandomizeGroup,SpawnIndex) +self:F({self.SpawnTemplatePrefix,Zone,RandomizeGroup,SpawnIndex}) +if Zone then +if RandomizeGroup then +return self:SpawnFromVec2(Zone:GetRandomVec2(),SpawnIndex) +else +return self:SpawnFromVec2(Zone:GetVec2(),SpawnIndex) +end +end +return nil +end +function SPAWN:InitUnControlled(UnControlled) +self:F2({self.SpawnTemplatePrefix,UnControlled}) +self.SpawnUnControlled=UnControlled +for SpawnGroupID=1,self.SpawnMaxGroups do +self.SpawnGroups[SpawnGroupID].UnControlled=UnControlled +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.SpawnTemplatePrefixself.SpawnAliasPrefix}) +self.SpawnIndex=self:_GetLastIndex() +for SpawnIndex=self.SpawnIndex,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:F({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 +SpawnTemplate=routines.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=self:_GetTemplate(SpawnTemplatePrefix) +SpawnTemplate.name=self:SpawnGroupName(SpawnIndex) +SpawnTemplate.groupId=nil +SpawnTemplate.lateActivation=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 +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=50,DelayOff={10,25},DelayOn={10,30}}, +Good={Evade=30,DelayOff={8,20},DelayOn={20,40}}, +High={Evade=15,DelayOff={5,17},DelayOn={30,50}}, +Excellent={Evade=10,DelayOff={3,10},DelayOn={30,60}} +}, +SEADGroupPrefixes={} +} +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.SEADGroupNames[SEADGroupPrefixes]=SEADGroupPrefixes +end +self:HandleEvent(EVENTS.Shot) +return self +end +function SEAD:OnEventShot(EventData) +self:F({EventData}) +local SEADUnit=EventData.IniDCSUnit +local SEADUnitName=EventData.IniDCSUnitName +local SEADWeapon=EventData.Weapon +local SEADWeaponName=EventData.WeaponName +self:T("Missile Launched = "..SEADWeaponName) +if SEADWeaponName=="KH-58"or SEADWeaponName=="KH-25MPU"or SEADWeaponName=="AGM-88"or SEADWeaponName=="KH-31A"or SEADWeaponName=="KH-31P"then +local _evade=math.random(1,100) +local _targetMim=EventData.Weapon:getTarget() +local _targetMimname=Unit.getName(_targetMim) +local _targetMimgroup=Unit.getGroup(Weapon.getTarget(SEADWeapon)) +local _targetMimgroupName=_targetMimgroup:getName() +local _targetMimcont=_targetMimgroup:getController() +local _targetskill=_DATABASE.Templates.Units[_targetMimname].Template.skill +self:T(self.SEADGroupPrefixes) +self:T(_targetMimgroupName) +local SEADGroupFound=false +for SEADGroupPrefixID,SEADGroupPrefix in pairs(self.SEADGroupPrefixes)do +if string.find(_targetMimgroupName,SEADGroupPrefix,1,true)then +SEADGroupFound=true +self:T('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("Evading, target skill "..string.format(_targetskill))) +local _targetMim=Weapon.getTarget(SEADWeapon) +local _targetMimname=Unit.getName(_targetMim) +local _targetMimgroup=Unit.getGroup(Weapon.getTarget(SEADWeapon)) +local _targetMimcont=_targetMimgroup:getController() +routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) +local SuppressedGroups1={} +local function SuppressionEnd1(id) +id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) +SuppressedGroups1[id.groupName]=nil +end +local id={ +groupName=_targetMimgroup, +ctrl=_targetMimcont +} +local delay1=math.random(self.TargetSkill[_targetskill].DelayOff[1],self.TargetSkill[_targetskill].DelayOff[2]) +if SuppressedGroups1[id.groupName]==nil then +SuppressedGroups1[id.groupName]={ +SuppressionEndTime1=timer.getTime()+delay1, +SuppressionEndN1=SuppressionEndCounter1 +} +Controller.setOption(_targetMimcont,AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) +timer.scheduleFunction(SuppressionEnd1,id,SuppressedGroups1[id.groupName].SuppressionEndTime1) +end +local SuppressedGroups={} +local function SuppressionEnd(id) +id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED) +SuppressedGroups[id.groupName]=nil +end +local id={ +groupName=_targetMimgroup, +ctrl=_targetMimcont +} +local delay=math.random(self.TargetSkill[_targetskill].DelayOn[1],self.TargetSkill[_targetskill].DelayOn[2]) +if SuppressedGroups[id.groupName]==nil then +SuppressedGroups[id.groupName]={ +SuppressionEndTime=timer.getTime()+delay, +SuppressionEndN=SuppressionEndCounter +} +timer.scheduleFunction(SuppressionEnd,id,SuppressedGroups[id.groupName].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_CLIENT:New(self.EscortClient,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_CLIENT:New(self.EscortClient,"Navigation",self.EscortMenu) +end +if not self.EscortMenuJoinUpAndFollow then +self.EscortMenuJoinUpAndFollow={} +end +self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1]=MENU_CLIENT_COMMAND:New(self.EscortClient,"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_CLIENT:New(self.EscortClient,"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_CLIENT_COMMAND +:New( +self.EscortClient, +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_CLIENT:New(self.EscortClient,"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_CLIENT_COMMAND +:New( +self.EscortClient, +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_CLIENT:New(self.EscortClient,"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_CLIENT_COMMAND +:New( +self.EscortClient, +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_CLIENT:New(self.EscortClient,"Navigation",self.EscortMenu) +end +local MenuText="" +if not MenuTextFormat then +MenuText="Flare" +else +MenuText=MenuTextFormat +end +if not self.EscortMenuFlare then +self.EscortMenuFlare=MENU_CLIENT:New(self.EscortClient,MenuText,self.EscortMenuReportNavigation,ESCORT._Flare,self) +self.EscortMenuFlareGreen=MENU_CLIENT_COMMAND:New(self.EscortClient,"Release green flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Green,"Released a green flare!") +self.EscortMenuFlareRed=MENU_CLIENT_COMMAND:New(self.EscortClient,"Release red flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Red,"Released a red flare!") +self.EscortMenuFlareWhite=MENU_CLIENT_COMMAND:New(self.EscortClient,"Release white flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.White,"Released a white flare!") +self.EscortMenuFlareYellow=MENU_CLIENT_COMMAND:New(self.EscortClient,"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_CLIENT:New(self.EscortClient,"Navigation",self.EscortMenu) +end +local MenuText="" +if not MenuTextFormat then +MenuText="Smoke" +else +MenuText=MenuTextFormat +end +if not self.EscortMenuSmoke then +self.EscortMenuSmoke=MENU_CLIENT:New(self.EscortClient,"Smoke",self.EscortMenuReportNavigation,ESCORT._Smoke,self) +self.EscortMenuSmokeGreen=MENU_CLIENT_COMMAND:New(self.EscortClient,"Release green smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Green,"Releasing green smoke!") +self.EscortMenuSmokeRed=MENU_CLIENT_COMMAND:New(self.EscortClient,"Release red smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Red,"Releasing red smoke!") +self.EscortMenuSmokeWhite=MENU_CLIENT_COMMAND:New(self.EscortClient,"Release white smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.White,"Releasing white smoke!") +self.EscortMenuSmokeOrange=MENU_CLIENT_COMMAND:New(self.EscortClient,"Release orange smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Orange,"Releasing orange smoke!") +self.EscortMenuSmokeBlue=MENU_CLIENT_COMMAND:New(self.EscortClient,"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_CLIENT:New(self.EscortClient,"Report targets",self.EscortMenu) +end +if not Seconds then +Seconds=30 +end +self.EscortMenuReportNearbyTargetsNow=MENU_CLIENT_COMMAND:New(self.EscortClient,"Report targets now!",self.EscortMenuReportNearbyTargets,ESCORT._ReportNearbyTargetsNow,self) +self.EscortMenuReportNearbyTargetsOn=MENU_CLIENT_COMMAND:New(self.EscortClient,"Report targets on",self.EscortMenuReportNearbyTargets,ESCORT._SwitchReportNearbyTargets,self,true) +self.EscortMenuReportNearbyTargetsOff=MENU_CLIENT_COMMAND:New(self.EscortClient,"Report targets off",self.EscortMenuReportNearbyTargets,ESCORT._SwitchReportNearbyTargets,self,false) +self.EscortMenuAttackNearbyTargets=MENU_CLIENT:New(self.EscortClient,"Attack targets",self.EscortMenu) +self.ReportTargetsScheduler=SCHEDULER:New(self,self._ReportTargetsScheduler,{},1,Seconds) +return self +end +function ESCORT:MenuAssistedAttack() +self:F() +self.EscortMenuTargetAssistance=MENU_CLIENT:New(self.EscortClient,"Request assistance from",self.EscortMenu) +return self +end +function ESCORT:MenuROE(MenuTextFormat) +self:F(MenuTextFormat) +if not self.EscortMenuROE then +self.EscortMenuROE=MENU_CLIENT:New(self.EscortClient,"ROE",self.EscortMenu) +if self.EscortGroup:OptionROEHoldFirePossible()then +self.EscortMenuROEHoldFire=MENU_CLIENT_COMMAND:New(self.EscortClient,"Hold Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEHoldFire(),"Holding weapons!") +end +if self.EscortGroup:OptionROEReturnFirePossible()then +self.EscortMenuROEReturnFire=MENU_CLIENT_COMMAND:New(self.EscortClient,"Return Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEReturnFire(),"Returning fire!") +end +if self.EscortGroup:OptionROEOpenFirePossible()then +self.EscortMenuROEOpenFire=MENU_CLIENT_COMMAND:New(self.EscortClient,"Open Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEOpenFire(),"Opening fire on designated targets!!") +end +if self.EscortGroup:OptionROEWeaponFreePossible()then +self.EscortMenuROEWeaponFree=MENU_CLIENT_COMMAND:New(self.EscortClient,"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_CLIENT:New(self.EscortClient,"Evasion",self.EscortMenu) +if self.EscortGroup:OptionROTNoReactionPossible()then +self.EscortMenuEvasionNoReaction=MENU_CLIENT_COMMAND:New(self.EscortClient,"Fight until death",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTNoReaction(),"Fighting until death!") +end +if self.EscortGroup:OptionROTPassiveDefensePossible()then +self.EscortMenuEvasionPassiveDefense=MENU_CLIENT_COMMAND:New(self.EscortClient,"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_CLIENT_COMMAND:New(self.EscortClient,"Evade enemy fire",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTEvadeFire(),"Evading on enemy fire!") +end +if self.EscortGroup:OptionROTVerticalPossible()then +self.EscortMenuOptionEvasionVertical=MENU_CLIENT_COMMAND:New(self.EscortClient,"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_CLIENT:New(self.EscortClient,"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(DetectedItemID) +local EscortGroup=self.EscortGroup +self:E(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:GetDetectedSet(DetectedItemID) +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:GetDetectedSet(DetectedItemID) +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,DetectedItemID) +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:GetDetectedSet(DetectedItemID) +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:GetDetectedSet(DetectedItemID) +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:E(DetectedItems) +local DetectedTargets=false +local DetectedMsgs={} +for ClientEscortGroupName,EscortGroupData in pairs(self.EscortClient._EscortGroups)do +local ClientEscortTargets=EscortGroupData.Detection +for DetectedItemID,DetectedItem in pairs(DetectedItems)do +self:E({DetectedItemID,DetectedItem}) +local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItemID,EscortGroupData.EscortGroup,_DATABASE:GetPlayerSettings(self.EscortClient:GetPlayerName())) +if ClientEscortGroupName==EscortGroupName then +local DetectedMsg=DetectedItemReportSummary:Text("\n") +DetectedMsgs[#DetectedMsgs+1]=DetectedMsg +self:T(DetectedMsg) +MENU_CLIENT_COMMAND:New(self.EscortClient, +DetectedMsg, +self.EscortMenuAttackNearbyTargets, +ESCORT._AttackTarget, +self, +DetectedItemID +) +else +if self.EscortMenuTargetAssistance then +local DetectedMsg=DetectedItemReportSummary:Text("\n") +self:T(DetectedMsg) +local MenuTargetAssistance=MENU_CLIENT:New(self.EscortClient,EscortGroupData.EscortName,self.EscortMenuTargetAssistance) +MENU_CLIENT_COMMAND:New(self.EscortClient, +DetectedMsg, +MenuTargetAssistance, +ESCORT._AssistTarget, +self, +EscortGroupData.EscortGroup, +DetectedItemID +) +end +end +DetectedTargets=true +end +end +self:E(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_CLIENT:New(Client,"Missile Trainer",nil) +Client.MenuMessages=MENU_CLIENT:New(Client,"Messages",Client.MainMenu) +Client.MenuOn=MENU_CLIENT_COMMAND:New(Client,"Messages On",Client.MenuMessages,self._MenuMessages,{MenuSelf=self,MessagesOnOff=true}) +Client.MenuOff=MENU_CLIENT_COMMAND:New(Client,"Messages Off",Client.MenuMessages,self._MenuMessages,{MenuSelf=self,MessagesOnOff=false}) +Client.MenuTracking=MENU_CLIENT:New(Client,"Tracking",Client.MainMenu) +Client.MenuTrackingToAll=MENU_CLIENT_COMMAND:New(Client,"To All",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingToAll=true}) +Client.MenuTrackingToTarget=MENU_CLIENT_COMMAND:New(Client,"To Target",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingToAll=false}) +Client.MenuTrackOn=MENU_CLIENT_COMMAND:New(Client,"Tracking On",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingOnOff=true}) +Client.MenuTrackOff=MENU_CLIENT_COMMAND:New(Client,"Tracking Off",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingOnOff=false}) +Client.MenuTrackIncrease=MENU_CLIENT_COMMAND:New(Client,"Frequency Increase",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingFrequency=-1}) +Client.MenuTrackDecrease=MENU_CLIENT_COMMAND:New(Client,"Frequency Decrease",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingFrequency=1}) +Client.MenuAlerts=MENU_CLIENT:New(Client,"Alerts",Client.MainMenu) +Client.MenuAlertsToAll=MENU_CLIENT_COMMAND:New(Client,"To All",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsToAll=true}) +Client.MenuAlertsToTarget=MENU_CLIENT_COMMAND:New(Client,"To Target",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsToAll=false}) +Client.MenuHitsOn=MENU_CLIENT_COMMAND:New(Client,"Hits On",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsHitsOnOff=true}) +Client.MenuHitsOff=MENU_CLIENT_COMMAND:New(Client,"Hits Off",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsHitsOnOff=false}) +Client.MenuLaunchesOn=MENU_CLIENT_COMMAND:New(Client,"Launches On",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsLaunchesOnOff=true}) +Client.MenuLaunchesOff=MENU_CLIENT_COMMAND:New(Client,"Launches Off",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsLaunchesOnOff=false}) +Client.MenuDetails=MENU_CLIENT:New(Client,"Details",Client.MainMenu) +Client.MenuDetailsDistanceOn=MENU_CLIENT_COMMAND:New(Client,"Range On",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsRangeOnOff=true}) +Client.MenuDetailsDistanceOff=MENU_CLIENT_COMMAND:New(Client,"Range Off",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsRangeOnOff=false}) +Client.MenuDetailsBearingOn=MENU_CLIENT_COMMAND:New(Client,"Bearing On",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsBearingOnOff=true}) +Client.MenuDetailsBearingOff=MENU_CLIENT_COMMAND:New(Client,"Bearing Off",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsBearingOnOff=false}) +Client.MenuDistance=MENU_CLIENT:New(Client,"Set distance to plane",Client.MainMenu) +Client.MenuDistance50=MENU_CLIENT_COMMAND:New(Client,"50 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=50/1000}) +Client.MenuDistance100=MENU_CLIENT_COMMAND:New(Client,"100 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=100/1000}) +Client.MenuDistance150=MENU_CLIENT_COMMAND:New(Client,"150 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=150/1000}) +Client.MenuDistance200=MENU_CLIENT_COMMAND:New(Client,"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:E("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 +AIRBASEPOLICE_BASE={ +ClassName="AIRBASEPOLICE_BASE", +SetClient=nil, +Airbases=nil, +AirbaseNames=nil, +} +function AIRBASEPOLICE_BASE:New(SetClient,Airbases) +local self=BASE:Inherit(self,BASE:New()) +self:E({self.ClassName,SetClient,Airbases}) +self.SetClient=SetClient +self.Airbases=Airbases +for AirbaseID,Airbase in pairs(self.Airbases)do +Airbase.ZoneBoundary=ZONE_POLYGON_BASE:New("Boundary",Airbase.PointsBoundary):SmokeZone(SMOKECOLOR.White):Flush() +for PointsRunwayID,PointsRunway in pairs(Airbase.PointsRunways)do +Airbase.ZoneRunways[PointsRunwayID]=ZONE_POLYGON_BASE:New("Runway "..PointsRunwayID,PointsRunway):SmokeZone(SMOKECOLOR.Red):Flush() +end +end +self.SetClient:ForEachClient( +function(Client) +Client:SetState(self,"Speeding",false) +Client:SetState(self,"Warnings",0) +Client:SetState(self,"Taxi",false) +end +) +self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{},0,2,0.05) +return self +end +function AIRBASEPOLICE_BASE:Monitor(AirbaseNames) +if AirbaseNames then +if type(AirbaseNames)=="table"then +self.AirbaseNames=AirbaseNames +else +self.AirbaseNames={AirbaseNames} +end +end +end +function AIRBASEPOLICE_BASE:_AirbaseMonitor() +for AirbaseID,Airbase in pairs(self.Airbases)do +if not self.AirbaseNames or self.AirbaseNames[AirbaseID]then +self:E(AirbaseID) +self.SetClient:ForEachClientInZone(Airbase.ZoneBoundary, +function(Client) +self:E(Client.UnitName) +if Client:IsAlive()then +local NotInRunwayZone=true +for ZoneRunwayID,ZoneRunway in pairs(Airbase.ZoneRunways)do +NotInRunwayZone=(Client:IsNotInZone(ZoneRunway)==true)and NotInRunwayZone or false +end +if NotInRunwayZone then +local Taxi=self:GetState(self,"Taxi") +self:E(Taxi) +if Taxi==false then +Client:Message("Welcome at "..AirbaseID..". The maximum taxiing speed is "..Airbase.MaximumSpeed" km/h.",20,"ATC") +self:SetState(self,"Taxi",true) +end +local VelocityVec3=Client:GetVelocity() +local Velocity=(VelocityVec3.x^2+VelocityVec3.y^2+VelocityVec3.z^2)^0.5 +local Velocity=Velocity*3.6 +local IsAboveRunway=Client:IsAboveRunway() +local IsOnGround=Client:InAir()==false +self:T(IsAboveRunway,IsOnGround) +if IsAboveRunway and IsOnGround then +if Velocity>Airbase.MaximumSpeed 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("You are speeding on the taxiway! Slow down or you will be removed from this airbase! Your current velocity is "..string.format("%2.0f km/h",Velocity),5,"Warning "..SpeedingWarnings.." / 3") +Client:SetState(self,"Warnings",SpeedingWarnings+1) +else +MESSAGE:New("Player "..Client:GetPlayerName().." is being damaged at the airbase, due to a speeding violation ...",10,"Airbase Police"):ToAll() +local function DestroyUntilHeavilyDamaged(Client) +local ClientCoord=Client:GetCoordinate() +ClientCoord:Explosion(100) +local Damage=Client:GetLife() +local InitialLife=Client:GetLife0() +MESSAGE:New("Player "..Client:GetPlayerName().." Damage ... "..Damage,5,"Airbase Police"):ToAll() +if(Damage/InitialLife)*100<80 then +Client:ScheduleStop(DestroyUntilHeavilyDamaged) +end +end +Client:ScheduleOnce(1,DestroyUntilHeavilyDamaged,Client) +trigger.action.setUserFlag("AIRCRAFT_"..Client:GetID(),100) +Client:SetState(self,"Speeding",false) +Client:SetState(self,"Warnings",0) +end +else +Client:Message("You are speeding on the taxiway, slow down now! Your current velocity is "..string.format("%2.0f km/h",Velocity),5,"Attention! ") +Client:SetState(self,"Speeding",true) +Client:SetState(self,"Warnings",1) +end +else +Client:SetState(self,"Speeding",false) +Client:SetState(self,"Warnings",0) +end +end +else +Client:SetState(self,"Speeding",false) +Client:SetState(self,"Warnings",0) +local Taxi=self:GetState(self,"Taxi") +if Taxi==true then +Client:Message("You have progressed to the runway ... Await take-off clearance ...",20,"ATC") +self:SetState(self,"Taxi",false) +end +end +end +end +) +end +end +return true +end +AIRBASEPOLICE_CAUCASUS={ +ClassName="AIRBASEPOLICE_CAUCASUS", +Airbases={ +AnapaVityazevo={ +PointsBoundary={ +[1]={["y"]=242234.85714287,["x"]=-6616.5714285726,}, +[2]={["y"]=241060.57142858,["x"]=-5585.142857144,}, +[3]={["y"]=243806.2857143,["x"]=-3962.2857142868,}, +[4]={["y"]=245240.57142858,["x"]=-4816.5714285726,}, +[5]={["y"]=244783.42857144,["x"]=-5630.8571428583,}, +[6]={["y"]=243800.57142858,["x"]=-5065.142857144,}, +[7]={["y"]=242232.00000001,["x"]=-6622.2857142868,}, +}, +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,} +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Batumi={ +PointsBoundary={ +[1]={["y"]=617567.14285714,["x"]=-355313.14285715,}, +[2]={["y"]=616181.42857142,["x"]=-354800.28571429,}, +[3]={["y"]=616007.14285714,["x"]=-355128.85714286,}, +[4]={["y"]=618230,["x"]=-356914.57142858,}, +[5]={["y"]=618727.14285714,["x"]=-356166,}, +[6]={["y"]=617572.85714285,["x"]=-355308.85714286,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Beslan={ +PointsBoundary={ +[1]={["y"]=842082.57142857,["x"]=-148445.14285715,}, +[2]={["y"]=845237.71428572,["x"]=-148639.71428572,}, +[3]={["y"]=845232,["x"]=-148765.42857143,}, +[4]={["y"]=844220.57142857,["x"]=-149168.28571429,}, +[5]={["y"]=843274.85714286,["x"]=-149125.42857143,}, +[6]={["y"]=842077.71428572,["x"]=-148554,}, +[7]={["y"]=842083.42857143,["x"]=-148445.42857143,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Gelendzhik={ +PointsBoundary={ +[1]={["y"]=297856.00000001,["x"]=-51151.428571429,}, +[2]={["y"]=299044.57142858,["x"]=-49720.000000001,}, +[3]={["y"]=298861.71428572,["x"]=-49580.000000001,}, +[4]={["y"]=298198.85714286,["x"]=-49842.857142858,}, +[5]={["y"]=297990.28571429,["x"]=-50151.428571429,}, +[6]={["y"]=297696.00000001,["x"]=-51054.285714286,}, +[7]={["y"]=297850.28571429,["x"]=-51160.000000001,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Gudauta={ +PointsBoundary={ +[1]={["y"]=517246.57142857,["x"]=-197850.28571429,}, +[2]={["y"]=516749.42857142,["x"]=-198070.28571429,}, +[3]={["y"]=515755.14285714,["x"]=-197598.85714286,}, +[4]={["y"]=515369.42857142,["x"]=-196538.85714286,}, +[5]={["y"]=515623.71428571,["x"]=-195618.85714286,}, +[6]={["y"]=515946.57142857,["x"]=-195510.28571429,}, +[7]={["y"]=517243.71428571,["x"]=-197858.85714286,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Kobuleti={ +PointsBoundary={ +[1]={["y"]=634427.71428571,["x"]=-318290.28571429,}, +[2]={["y"]=635033.42857143,["x"]=-317550.2857143,}, +[3]={["y"]=635864.85714286,["x"]=-317333.14285715,}, +[4]={["y"]=636967.71428571,["x"]=-317261.71428572,}, +[5]={["y"]=637144.85714286,["x"]=-317913.14285715,}, +[6]={["y"]=634630.57142857,["x"]=-318687.42857144,}, +[7]={["y"]=634424.85714286,["x"]=-318290.2857143,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +KrasnodarCenter={ +PointsBoundary={ +[1]={["y"]=366680.28571429,["x"]=11699.142857142,}, +[2]={["y"]=366654.28571429,["x"]=11225.142857142,}, +[3]={["y"]=367497.14285715,["x"]=11082.285714285,}, +[4]={["y"]=368025.71428572,["x"]=10396.57142857,}, +[5]={["y"]=369854.28571429,["x"]=11367.999999999,}, +[6]={["y"]=369840.00000001,["x"]=11910.857142856,}, +[7]={["y"]=366682.57142858,["x"]=11697.999999999,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +KrasnodarPashkovsky={ +PointsBoundary={ +[1]={["y"]=386754,["x"]=6476.5714285703,}, +[2]={["y"]=389182.57142858,["x"]=8722.2857142846,}, +[3]={["y"]=388832.57142858,["x"]=9086.5714285703,}, +[4]={["y"]=386961.14285715,["x"]=7707.9999999989,}, +[5]={["y"]=385404,["x"]=9179.4285714274,}, +[6]={["y"]=383239.71428572,["x"]=7386.5714285703,}, +[7]={["y"]=383954,["x"]=6486.5714285703,}, +[8]={["y"]=385775.42857143,["x"]=8097.9999999989,}, +[9]={["y"]=386804,["x"]=7319.4285714274,}, +[10]={["y"]=386375.42857143,["x"]=6797.9999999989,}, +[11]={["y"]=386746.85714286,["x"]=6472.2857142846,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Krymsk={ +PointsBoundary={ +[1]={["y"]=293338.00000001,["x"]=-7575.4285714297,}, +[2]={["y"]=295199.42857144,["x"]=-5434.0000000011,}, +[3]={["y"]=295595.14285715,["x"]=-6239.7142857154,}, +[4]={["y"]=294152.2857143,["x"]=-8325.4285714297,}, +[5]={["y"]=293345.14285715,["x"]=-7596.8571428582,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Kutaisi={ +PointsBoundary={ +[1]={["y"]=682087.42857143,["x"]=-284512.85714286,}, +[2]={["y"]=685387.42857143,["x"]=-283662.85714286,}, +[3]={["y"]=685294.57142857,["x"]=-284977.14285715,}, +[4]={["y"]=682744.57142857,["x"]=-286505.71428572,}, +[5]={["y"]=682094.57142857,["x"]=-284527.14285715,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +MaykopKhanskaya={ +PointsBoundary={ +[1]={["y"]=456876.28571429,["x"]=-27665.42857143,}, +[2]={["y"]=457800,["x"]=-28392.857142858,}, +[3]={["y"]=459368.57142857,["x"]=-26378.571428573,}, +[4]={["y"]=459425.71428572,["x"]=-25242.857142858,}, +[5]={["y"]=458961.42857143,["x"]=-24964.285714287,}, +[6]={["y"]=456878.57142857,["x"]=-27667.714285715,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +MineralnyeVody={ +PointsBoundary={ +[1]={["y"]=703857.14285714,["x"]=-50226.000000002,}, +[2]={["y"]=707385.71428571,["x"]=-51911.714285716,}, +[3]={["y"]=707595.71428571,["x"]=-51434.857142859,}, +[4]={["y"]=707900,["x"]=-51568.857142859,}, +[5]={["y"]=707542.85714286,["x"]=-52326.000000002,}, +[6]={["y"]=706628.57142857,["x"]=-52568.857142859,}, +[7]={["y"]=705142.85714286,["x"]=-51790.285714288,}, +[8]={["y"]=703678.57142857,["x"]=-50611.714285716,}, +[9]={["y"]=703857.42857143,["x"]=-50226.857142859,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Mozdok={ +PointsBoundary={ +[1]={["y"]=832123.42857143,["x"]=-83608.571428573,}, +[2]={["y"]=835916.28571429,["x"]=-83144.285714288,}, +[3]={["y"]=835474.28571429,["x"]=-84170.571428573,}, +[4]={["y"]=832911.42857143,["x"]=-84470.571428573,}, +[5]={["y"]=832487.71428572,["x"]=-85565.714285716,}, +[6]={["y"]=831573.42857143,["x"]=-85351.42857143,}, +[7]={["y"]=832123.71428572,["x"]=-83610.285714288,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Nalchik={ +PointsBoundary={ +[1]={["y"]=759370,["x"]=-125502.85714286,}, +[2]={["y"]=761384.28571429,["x"]=-124177.14285714,}, +[3]={["y"]=761472.85714286,["x"]=-124325.71428572,}, +[4]={["y"]=761092.85714286,["x"]=-125048.57142857,}, +[5]={["y"]=760295.71428572,["x"]=-125685.71428572,}, +[6]={["y"]=759444.28571429,["x"]=-125734.28571429,}, +[7]={["y"]=759375.71428572,["x"]=-125511.42857143,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Novorossiysk={ +PointsBoundary={ +[1]={["y"]=278677.71428573,["x"]=-41656.571428572,}, +[2]={["y"]=278446.2857143,["x"]=-41453.714285715,}, +[3]={["y"]=278989.14285716,["x"]=-40188.000000001,}, +[4]={["y"]=279717.71428573,["x"]=-39968.000000001,}, +[5]={["y"]=280020.57142859,["x"]=-40208.000000001,}, +[6]={["y"]=278674.85714287,["x"]=-41660.857142858,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +SenakiKolkhi={ +PointsBoundary={ +[1]={["y"]=646036.57142857,["x"]=-281778.85714286,}, +[2]={["y"]=646045.14285714,["x"]=-281191.71428571,}, +[3]={["y"]=647032.28571429,["x"]=-280598.85714285,}, +[4]={["y"]=647669.42857143,["x"]=-281273.14285714,}, +[5]={["y"]=648323.71428571,["x"]=-281370.28571428,}, +[6]={["y"]=648520.85714286,["x"]=-281978.85714285,}, +[7]={["y"]=646039.42857143,["x"]=-281783.14285714,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +SochiAdler={ +PointsBoundary={ +[1]={["y"]=460642.28571428,["x"]=-164861.71428571,}, +[2]={["y"]=462820.85714285,["x"]=-163368.85714286,}, +[3]={["y"]=463649.42857142,["x"]=-163340.28571429,}, +[4]={["y"]=463835.14285714,["x"]=-164040.28571429,}, +[5]={["y"]=462535.14285714,["x"]=-165654.57142857,}, +[6]={["y"]=460678,["x"]=-165247.42857143,}, +[7]={["y"]=460635.14285714,["x"]=-164876,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Soganlug={ +PointsBoundary={ +[1]={["y"]=894530.85714286,["x"]=-316928.28571428,}, +[2]={["y"]=896422.28571428,["x"]=-318622.57142857,}, +[3]={["y"]=896090.85714286,["x"]=-318934,}, +[4]={["y"]=894019.42857143,["x"]=-317119.71428571,}, +[5]={["y"]=894533.71428571,["x"]=-316925.42857143,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +SukhumiBabushara={ +PointsBoundary={ +[1]={["y"]=562541.14285714,["x"]=-219852.28571429,}, +[2]={["y"]=562691.14285714,["x"]=-219395.14285714,}, +[3]={["y"]=564326.85714286,["x"]=-219523.71428571,}, +[4]={["y"]=566262.57142857,["x"]=-221166.57142857,}, +[5]={["y"]=566069.71428571,["x"]=-221580.85714286,}, +[6]={["y"]=562534,["x"]=-219873.71428571,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +TbilisiLochini={ +PointsBoundary={ +[1]={["y"]=895172.85714286,["x"]=-314667.42857143,}, +[2]={["y"]=895337.42857143,["x"]=-314143.14285714,}, +[3]={["y"]=895990.28571429,["x"]=-314036,}, +[4]={["y"]=897730.28571429,["x"]=-315284.57142857,}, +[5]={["y"]=897901.71428571,["x"]=-316284.57142857,}, +[6]={["y"]=897684.57142857,["x"]=-316618.85714286,}, +[7]={["y"]=895173.14285714,["x"]=-314667.42857143,}, +}, +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,} +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Vaziani={ +PointsBoundary={ +[1]={["y"]=902122,["x"]=-318163.71428572,}, +[2]={["y"]=902678.57142857,["x"]=-317594,}, +[3]={["y"]=903275.71428571,["x"]=-317405.42857143,}, +[4]={["y"]=903418.57142857,["x"]=-317891.14285714,}, +[5]={["y"]=904292.85714286,["x"]=-318748.28571429,}, +[6]={["y"]=904542,["x"]=-319740.85714286,}, +[7]={["y"]=904042,["x"]=-320166.57142857,}, +[8]={["y"]=902121.42857143,["x"]=-318164.85714286,}, +}, +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,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +}, +} +function AIRBASEPOLICE_CAUCASUS:New(SetClient) +local self=BASE:Inherit(self,AIRBASEPOLICE_BASE:New(SetClient,self.Airbases)) +return self +end +AIRBASEPOLICE_NEVADA={ +ClassName="AIRBASEPOLICE_NEVADA", +Airbases={ +Nellis={ +PointsBoundary={ +[1]={["y"]=-17814.714285714,["x"]=-399823.14285714,}, +[2]={["y"]=-16875.857142857,["x"]=-398763.14285714,}, +[3]={["y"]=-16251.571428571,["x"]=-398988.85714286,}, +[4]={["y"]=-16163,["x"]=-398693.14285714,}, +[5]={["y"]=-16328.714285714,["x"]=-398034.57142857,}, +[6]={["y"]=-15943,["x"]=-397571.71428571,}, +[7]={["y"]=-15711.571428571,["x"]=-397551.71428571,}, +[8]={["y"]=-15748.714285714,["x"]=-396806,}, +[9]={["y"]=-16288.714285714,["x"]=-396517.42857143,}, +[10]={["y"]=-16751.571428571,["x"]=-396308.85714286,}, +[11]={["y"]=-17263,["x"]=-396234.57142857,}, +[12]={["y"]=-17577.285714286,["x"]=-396640.28571429,}, +[13]={["y"]=-17614.428571429,["x"]=-397400.28571429,}, +[14]={["y"]=-19405.857142857,["x"]=-399428.85714286,}, +[15]={["y"]=-19234.428571429,["x"]=-399683.14285714,}, +[16]={["y"]=-18708.714285714,["x"]=-399408.85714286,}, +[17]={["y"]=-18397.285714286,["x"]=-399657.42857143,}, +[18]={["y"]=-17814.428571429,["x"]=-399823.42857143,}, +}, +PointsRunways={ +[1]={ +[1]={["y"]=-18687,["x"]=-399380.28571429,}, +[2]={["y"]=-18620.714285714,["x"]=-399436.85714286,}, +[3]={["y"]=-16217.857142857,["x"]=-396596.85714286,}, +[4]={["y"]=-16300.142857143,["x"]=-396530,}, +[5]={["y"]=-18687,["x"]=-399380.85714286,}, +}, +[2]={ +[1]={["y"]=-18451.571428572,["x"]=-399580.57142857,}, +[2]={["y"]=-18392.142857143,["x"]=-399628.57142857,}, +[3]={["y"]=-16011,["x"]=-396806.85714286,}, +[4]={["y"]=-16074.714285714,["x"]=-396751.71428572,}, +[5]={["y"]=-18451.571428572,["x"]=-399580.85714285,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +McCarran={ +PointsBoundary={ +[1]={["y"]=-29455.285714286,["x"]=-416277.42857142,}, +[2]={["y"]=-28860.142857143,["x"]=-416492,}, +[3]={["y"]=-25044.428571429,["x"]=-416344.85714285,}, +[4]={["y"]=-24580.142857143,["x"]=-415959.14285714,}, +[5]={["y"]=-25073,["x"]=-415630.57142857,}, +[6]={["y"]=-25087.285714286,["x"]=-415130.57142857,}, +[7]={["y"]=-25830.142857143,["x"]=-414866.28571428,}, +[8]={["y"]=-26658.714285715,["x"]=-414880.57142857,}, +[9]={["y"]=-26973,["x"]=-415273.42857142,}, +[10]={["y"]=-27380.142857143,["x"]=-415187.71428571,}, +[11]={["y"]=-27715.857142857,["x"]=-414144.85714285,}, +[12]={["y"]=-27551.571428572,["x"]=-413473.42857142,}, +[13]={["y"]=-28630.142857143,["x"]=-413201.99999999,}, +[14]={["y"]=-29494.428571429,["x"]=-415437.71428571,}, +[15]={["y"]=-29455.571428572,["x"]=-416277.71428571,}, +}, +PointsRunways={ +[1]={ +[1]={["y"]=-29408.428571429,["x"]=-416016.28571428,}, +[2]={["y"]=-29408.142857144,["x"]=-416105.42857142,}, +[3]={["y"]=-24680.714285715,["x"]=-416003.14285713,}, +[4]={["y"]=-24681.857142858,["x"]=-415926.57142856,}, +[5]={["y"]=-29408.42857143,["x"]=-416016.57142856,}, +}, +[2]={ +[1]={["y"]=-28575.571428572,["x"]=-416303.14285713,}, +[2]={["y"]=-28575.571428572,["x"]=-416382.57142856,}, +[3]={["y"]=-25111.000000001,["x"]=-416309.7142857,}, +[4]={["y"]=-25111.000000001,["x"]=-416249.14285713,}, +[5]={["y"]=-28575.571428572,["x"]=-416303.7142857,}, +}, +[3]={ +[1]={["y"]=-29331.000000001,["x"]=-416275.42857141,}, +[2]={["y"]=-29259.000000001,["x"]=-416306.85714284,}, +[3]={["y"]=-28005.571428572,["x"]=-413449.7142857,}, +[4]={["y"]=-28068.714285715,["x"]=-413422.85714284,}, +[5]={["y"]=-29331.000000001,["x"]=-416275.7142857,}, +}, +[4]={ +[1]={["y"]=-29073.285714286,["x"]=-416386.57142856,}, +[2]={["y"]=-28997.285714286,["x"]=-416417.42857141,}, +[3]={["y"]=-27697.571428572,["x"]=-413464.57142856,}, +[4]={["y"]=-27767.857142858,["x"]=-413434.28571427,}, +[5]={["y"]=-29073.000000001,["x"]=-416386.85714284,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +Creech={ +PointsBoundary={ +[1]={["y"]=-74522.714285715,["x"]=-360887.99999998,}, +[2]={["y"]=-74197,["x"]=-360556.57142855,}, +[3]={["y"]=-74402.714285715,["x"]=-359639.42857141,}, +[4]={["y"]=-74637,["x"]=-359279.42857141,}, +[5]={["y"]=-75759.857142857,["x"]=-359005.14285712,}, +[6]={["y"]=-75834.142857143,["x"]=-359045.14285712,}, +[7]={["y"]=-75902.714285714,["x"]=-359782.28571427,}, +[8]={["y"]=-76099.857142857,["x"]=-360399.42857141,}, +[9]={["y"]=-77314.142857143,["x"]=-360219.42857141,}, +[10]={["y"]=-77728.428571429,["x"]=-360445.14285713,}, +[11]={["y"]=-77585.571428571,["x"]=-360585.14285713,}, +[12]={["y"]=-76471.285714286,["x"]=-360819.42857141,}, +[13]={["y"]=-76325.571428571,["x"]=-360942.28571427,}, +[14]={["y"]=-74671.857142857,["x"]=-360927.7142857,}, +[15]={["y"]=-74522.714285714,["x"]=-360888.85714284,}, +}, +PointsRunways={ +[1]={ +[1]={["y"]=-74237.571428571,["x"]=-360591.7142857,}, +[2]={["y"]=-74234.428571429,["x"]=-360493.71428571,}, +[3]={["y"]=-77605.285714286,["x"]=-360399.14285713,}, +[4]={["y"]=-77608.714285715,["x"]=-360498.85714285,}, +[5]={["y"]=-74237.857142857,["x"]=-360591.7142857,}, +}, +[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,}, +[5]={["y"]=-75807.285714287,["x"]=-359073.42857142,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +GroomLake={ +PointsBoundary={ +[1]={["y"]=-88916.714285714,["x"]=-289102.28571425,}, +[2]={["y"]=-87023.571428572,["x"]=-290388.57142857,}, +[3]={["y"]=-85916.428571429,["x"]=-290674.28571428,}, +[4]={["y"]=-87645.000000001,["x"]=-286567.14285714,}, +[5]={["y"]=-88380.714285715,["x"]=-286388.57142857,}, +[6]={["y"]=-89670.714285715,["x"]=-283524.28571428,}, +[7]={["y"]=-89797.857142858,["x"]=-283567.14285714,}, +[8]={["y"]=-88635.000000001,["x"]=-286749.99999999,}, +[9]={["y"]=-89177.857142858,["x"]=-287207.14285714,}, +[10]={["y"]=-89092.142857144,["x"]=-288892.85714285,}, +[11]={["y"]=-88917.000000001,["x"]=-289102.85714285,}, +}, +PointsRunways={ +[1]={ +[1]={["y"]=-86039.000000001,["x"]=-290606.28571428,}, +[2]={["y"]=-85965.285714287,["x"]=-290573.99999999,}, +[3]={["y"]=-87692.714285715,["x"]=-286634.85714285,}, +[4]={["y"]=-87756.714285715,["x"]=-286663.99999999,}, +[5]={["y"]=-86038.714285715,["x"]=-290606.85714285,}, +}, +[2]={ +[1]={["y"]=-86808.428571429,["x"]=-290375.7142857,}, +[2]={["y"]=-86732.714285715,["x"]=-290344.28571427,}, +[3]={["y"]=-89672.714285714,["x"]=-283546.57142855,}, +[4]={["y"]=-89772.142857143,["x"]=-283587.71428569,}, +[5]={["y"]=-86808.142857143,["x"]=-290375.7142857,}, +}, +}, +ZoneBoundary={}, +ZoneRunways={}, +MaximumSpeed=50, +}, +}, +} +function AIRBASEPOLICE_NEVADA:New(SetClient) +local self=BASE:Inherit(self,AIRBASEPOLICE_BASE:New(SetClient,self.Airbases)) +end +do +DETECTION_BASE={ +ClassName="DETECTION_BASE", +DetectionSetGroup=nil, +DetectionRange=nil, +DetectedObjects={}, +DetectionRun=0, +DetectedObjectsIdentified={}, +DetectedItems={}, +} +function DETECTION_BASE:New(DetectionSetGroup) +local self=BASE:Inherit(self,FSM:New()) +self.DetectedItemCount=0 +self.DetectedItemMax=0 +self.DetectedItems={} +self.DetectionSetGroup=DetectionSetGroup +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","DetectionGroup","Detecting") +self:AddTransition("Detecting","Detected","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) +self:E({From,Event,To}) +local DetectDelay=0.1 +self.DetectionCount=0 +self.DetectionRun=0 +self:UnIdentifyAllDetectedObjects() +local DetectionTimeStamp=timer.getTime() +for DetectionGroupID,DetectionGroupData in pairs(self.DetectionSetGroup:GetSet())do +self:__DetectionGroup(DetectDelay,DetectionGroupData,DetectionTimeStamp) +self.DetectionCount=self.DetectionCount+1 +DetectDelay=DetectDelay+1 +end +end +function DETECTION_BASE:onafterDetectionGroup(From,Event,To,DetectionGroup,DetectionTimeStamp) +self:E({From,Event,To}) +self.DetectionRun=self.DetectionRun+1 +local HasDetectedObjects=false +if DetectionGroup:IsAlive()then +self:T({"DetectionGroup is Alive",DetectionGroup:GetName()}) +local DetectionGroupName=DetectionGroup:GetName() +local DetectionUnit=DetectionGroup:GetUnit(1) +local DetectedUnits={} +local DetectedTargets=DetectionGroup:GetDetectedTargets( +self.DetectVisual, +self.DetectOptical, +self.DetectRadar, +self.DetectIRST, +self.DetectRWR, +self.DetectDLINK +) +self:F(DetectedTargets) +for DetectionObjectID,Detection in pairs(DetectedTargets)do +local DetectedObject=Detection.object +if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then +local TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity=DetectionUnit:IsTargetDetected( +DetectedObject, +self.DetectVisual, +self.DetectOptical, +self.DetectRadar, +self.DetectIRST, +self.DetectRWR, +self.DetectDLINK +) +self:T2({TargetIsDetected=TargetIsDetected,TargetIsVisible=TargetIsVisible,TargetLastTime=TargetLastTime,TargetKnowType=TargetKnowType,TargetKnowDistance=TargetKnowDistance,TargetLastPos=TargetLastPos,TargetLastVelocity=TargetLastVelocity}) +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=DetectionGroup: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 +self:F({"Detected Target:",DetectionGroupName,DetectedObjectName,DetectedObjectType,Distance,DetectedUnitCategory}) +DetectionAccepted=self._.FilterCategories[DetectedUnitCategory]~=nil and DetectionAccepted or false +if self.AcceptRange and Distance>self.AcceptRange then +DetectionAccepted=false +end +if self.AcceptZones then +for AcceptZoneID,AcceptZone in pairs(self.AcceptZones)do +local AcceptZone=AcceptZone +if AcceptZone:IsVec2InZone(DetectedObjectVec2)==false then +DetectionAccepted=false +end +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 Detection.visible 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() +self:T({Probability,DistanceProbability}) +if Probability>DistanceProbability then +DetectionAccepted=false +end +end +if not self.DetectedObjects[DetectedObjectName]and Detection.visible 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() +self:T({Probability,AlphaAngleProbability}) +if Probability>AlphaAngleProbability then +DetectionAccepted=false +end +end +if not self.DetectedObjects[DetectedObjectName]and Detection.visible and self.ZoneProbability then +for ZoneDataID,ZoneData in pairs(self.ZoneProbability)do +self:E({ZoneData}) +local ZoneObject=ZoneData[1] +local ZoneProbability=ZoneData[2] +ZoneProbability=ZoneProbability*30/300 +if ZoneObject:IsPointVec2InZone(DetectedObjectVec2)==true then +local Probability=math.random() +self:T({Probability,ZoneProbability}) +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 +self.DetectedObjects[DetectedObjectName].IsDetected=TargetIsDetected +self.DetectedObjects[DetectedObjectName].IsVisible=TargetIsVisible +self.DetectedObjects[DetectedObjectName].LastTime=TargetLastTime +self.DetectedObjects[DetectedObjectName].LastPos=TargetLastPos +self.DetectedObjects[DetectedObjectName].LastVelocity=TargetLastVelocity +self.DetectedObjects[DetectedObjectName].KnowType=TargetKnowType +self.DetectedObjects[DetectedObjectName].KnowDistance=Detection.distance +self.DetectedObjects[DetectedObjectName].Distance=Distance +self.DetectedObjects[DetectedObjectName].DetectionTimeStamp=DetectionTimeStamp +self:F({DetectedObject=self.DetectedObjects[DetectedObjectName]}) +local DetectedUnit=UNIT:FindByName(DetectedObjectName) +DetectedUnits[DetectedObjectName]=DetectedUnit +else +if self.DetectedObjects[DetectedObjectName]then +self.DetectedObjects[DetectedObjectName]=nil +end +end +end +self:T2(self.DetectedObjects) +end +if HasDetectedObjects then +self:__Detected(0.1,DetectedUnits) +end +end +if self.DetectionCount>0 and self.DetectionRun==self.DetectionCount then +self:T("--> Create Detection Sets") +for DetectedObjectName,DetectedObject in pairs(self.DetectedObjects)do +if self.DetectedObjects[DetectedObjectName].IsDetected==true and self.DetectedObjects[DetectedObjectName].DetectionTimeStamp+60<=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) +end +self:__Detect(self.RefreshTimeInterval) +end +end +end +do +function DETECTION_BASE:CleanDetectionItem(DetectedItem,DetectedItemID) +self:F2() +local DetectedSet=DetectedItem.Set +if DetectedSet:Count()==0 then +self:RemoveDetectedItem(DetectedItemID) +end +return self +end +function DETECTION_BASE:ForgetDetectedUnit(UnitName) +self:F2() +local DetectedItems=self:GetDetectedItems() +for DetectedItemIndex,DetectedItem in pairs(DetectedItems)do +local DetectedSet=self:GetDetectedSet(DetectedItemIndex) +if DetectedSet then +DetectedSet:RemoveUnitsByName(UnitName) +end +end +return self +end +function DETECTION_BASE:CreateDetectionItems() +self:F2() +self:E("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:E({"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:E({"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:E({"Change on Detection Item:",DetectedItem.ID,ChangeCode,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:E({"Change on Detection Item:",DetectedItem.ID,ChangeCode,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) +return DetectedItem.FriendliesNearBy~=nil or false +end +function DETECTION_BASE:GetFriendliesNearBy(DetectedItem) +return DetectedItem.FriendliesNearBy +end +function DETECTION_BASE:FilterFriendliesCategory(FriendliesCategory) +self.FriendliesCategory=FriendliesCategory +return self +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(ReportGroupData) +self:F2() +local DetectedItem=ReportGroupData.DetectedItem +local DetectedSet=ReportGroupData.DetectedItem.Set +local DetectedUnit=DetectedSet:GetFirst() +DetectedItem.FriendliesNearBy=nil +if DetectedUnit and DetectedUnit:IsAlive()then +local DetectedUnitCoord=DetectedUnit:GetCoordinate() +local InterceptCoord=ReportGroupData.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 FoundUnitName=FoundDCSUnit:getName() +local FoundUnitGroupName=FoundDCSUnit:getGroup():getName() +local EnemyUnitName=DetectedUnit:GetName() +local FoundUnitInReportSetGroup=ReportSetGroup:FindGroup(FoundUnitGroupName)~=nil +self:T({"Friendlies search:",FoundUnitName,FoundUnitCoalition,EnemyUnitName,EnemyCoalition,FoundUnitInReportSetGroup}) +if FoundUnitInReportSetGroup==true then +for PrefixID,Prefix in pairs(self.FriendlyPrefixes or{})do +self:F({"FriendlyPrefix:",Prefix}) +if string.find(FoundUnitName,Prefix:gsub("-","%%-"),1)then +FoundUnitInReportSetGroup=false +break +end +end +end +self:F({"Friendlies search:",FoundUnitName,FoundUnitCoalition,EnemyUnitName,EnemyCoalition,FoundUnitInReportSetGroup}) +if FoundUnitCoalition~=EnemyCoalition and FoundUnitInReportSetGroup==false then +local FriendlyUnit=UNIT:Find(FoundDCSUnit) +local FriendlyUnitName=FriendlyUnit:GetName() +local FriendlyUnitCategory=FriendlyUnit:GetDesc().category +self:T({FriendlyUnitCategory=FriendlyUnitCategory,FriendliesCategory=self.FriendliesCategory}) +DetectedItem.FriendliesNearBy=DetectedItem.FriendliesNearBy or{} +DetectedItem.FriendliesNearBy[FriendlyUnitName]=FriendlyUnit +local Distance=DetectedUnitCoord:Get2DDistance(FriendlyUnit:GetCoordinate()) +DetectedItem.FriendliesDistance=DetectedItem.FriendliesDistance or{} +DetectedItem.FriendliesDistance[Distance]=FriendlyUnit +self:T({FriendlyUnitName=FriendlyUnitName,Distance=Distance}) +return true +end +return true +end +world.searchObjects(Object.Category.UNIT,SphereSearch,FindNearByFriendlies,ReportGroupData) +DetectedItem.PlayersNearBy=nil +local DetectionZone=ZONE_UNIT:New("DetectionPlayers",DetectedUnit,self.FriendliesRange) +_DATABASE:ForEachPlayer( +function(PlayerUnitName) +local PlayerUnit=UNIT:FindByName(PlayerUnitName) +if PlayerUnit and PlayerUnit:IsInZone(DetectionZone)then +local PlayerUnitCategory=PlayerUnit:GetDesc().category +if(not self.FriendliesCategory)or(self.FriendliesCategory and(self.FriendliesCategory==PlayerUnitCategory))then +DetectedItem.FriendliesNearBy=DetectedItem.FriendliesNearBy or{} +local PlayerUnitName=PlayerUnit:GetName() +DetectedItem.PlayersNearBy=DetectedItem.PlayersNearBy or{} +DetectedItem.PlayersNearBy[PlayerUnitName]=PlayerUnit +DetectedItem.FriendliesNearBy=DetectedItem.FriendliesNearBy or{} +DetectedItem.FriendliesNearBy[PlayerUnitName]=PlayerUnit +local Distance=DetectedUnitCoord:Get2DDistance(PlayerUnit:GetCoordinate()) +DetectedItem.FriendliesDistance=DetectedItem.FriendliesDistance or{} +DetectedItem.FriendliesDistance[Distance]=PlayerUnit +end +end +end +) +end +end +end +function DETECTION_BASE:IsDetectedObjectIdentified(DetectedObject) +local DetectedObjectName=DetectedObject.Name +if DetectedObjectName then +local DetectedObjectIdentified=self.DetectedObjectsIdentified[DetectedObjectName]==true +self:T3(DetectedObjectIdentified) +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) +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,DetectedItemIndex,Set) +local DetectedItem={} +self.DetectedItemCount=self.DetectedItemCount+1 +self.DetectedItemMax=self.DetectedItemMax+1 +if DetectedItemIndex then +self.DetectedItems[DetectedItemIndex]=DetectedItem +else +self.DetectedItems[self.DetectedItemCount]=DetectedItem +end +DetectedItem.Set=Set or SET_UNIT:New():FilterDeads():FilterCrashes() +DetectedItem.Index=DetectedItemIndex or self.DetectedItemCount +DetectedItem.ItemID=ItemPrefix.."."..self.DetectedItemMax +DetectedItem.ID=self.DetectedItemMax +DetectedItem.Removed=false +return DetectedItem +end +function DETECTION_BASE:AddDetectedItemZone(DetectedItemIndex,Set,Zone) +local DetectedItem=self:AddDetectedItem("AREA",DetectedItemIndex,Set) +DetectedItem.Zone=Zone +return DetectedItem +end +function DETECTION_BASE:RemoveDetectedItem(DetectedItemIndex) +if self.DetectedItems[DetectedItemIndex]then +self.DetectedItemCount=self.DetectedItemCount-1 +self.DetectedItems[DetectedItemIndex]=nil +end +end +function DETECTION_BASE:GetDetectedItems() +return self.DetectedItems +end +function DETECTION_BASE:GetDetectedItemsCount() +local DetectedCount=self.DetectedItemCount +return DetectedCount +end +function DETECTION_BASE:GetDetectedItem(Index) +local DetectedItem=self.DetectedItems[Index] +if DetectedItem then +return DetectedItem +end +return nil +end +function DETECTION_BASE:GetDetectedItemID(Index) +local DetectedItem=self.DetectedItems[Index] +if DetectedItem then +return DetectedItem.ItemID +end +return"" +end +function DETECTION_BASE:GetDetectedID(Index) +local DetectedItem=self.DetectedItems[Index] +if DetectedItem then +return DetectedItem.ID +end +return"" +end +function DETECTION_BASE:GetDetectedSet(Index) +local DetectedItem=self:GetDetectedItem(Index) +local DetectedSetUnit=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(Index) +local DetectedZone=self.DetectedItems[Index].Zone +if DetectedZone then +return DetectedZone +end +local Detected +return nil +end +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(Index) +self:F({Index=Index}) +local DetectedItem=self:GetDetectedItem(Index) +if DetectedItem then +return DetectedItem.Coordinate +end +return nil +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(Index) +self:F({Index=Index}) +local DetectedItem=self:GetDetectedItem(Index) +if DetectedItem then +return DetectedItem.ThreatLevel or 0,DetectedItem.ThreatText or"" +end +return nil,"" +end +function DETECTION_BASE:DetectedItemMenu(Index,AttackGroup) +self:F(Index) +return nil +end +function DETECTION_BASE:DetectedItemReportSummary(Index,AttackGroup,Settings) +self:F(Index) +return nil +end +function DETECTION_BASE:DetectedReportDetailed(AttackGroup) +self:F() +return nil +end +function DETECTION_BASE:GetDetectionSetGroup() +local DetectionSetGroup=self.DetectionSetGroup +return DetectionSetGroup +end +function DETECTION_BASE:Schedule(DelayTime,RepeatInterval) +self:F2() +self.ScheduleDelayTime=DelayTime +self.ScheduleRepeatInterval=RepeatInterval +self.DetectionScheduler=SCHEDULER:New(self,self._DetectionScheduler,{self,"Detection"},DelayTime,RepeatInterval) +return self +end +end +do +DETECTION_UNITS={ +ClassName="DETECTION_UNITS", +DetectionRange=nil, +} +function DETECTION_UNITS:New(DetectionSetGroup) +local self=BASE:Inherit(self,DETECTION_BASE:New(DetectionSetGroup)) +self._SmokeDetectedUnits=false +self._FlareDetectedUnits=false +self._SmokeDetectedZones=false +self._FlareDetectedZones=false +self._BoundDetectedZones=false +return self +end +function DETECTION_UNITS:GetChangeText(DetectedItem) +self:F(DetectedItem) +local MT={} +for ChangeCode,ChangeData in pairs(DetectedItem.Changes)do +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]=" New target(s) detected: "..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]=" Invisible or destroyed target(s): "..table.concat(MTUT,", ").."." +end +end +return table.concat(MT,"\n") +end +function DETECTION_UNITS:CreateDetectionItems() +self:F2(#self.DetectedObjects) +for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do +local DetectedItemSet=DetectedItem.Set +for DetectedUnitName,DetectedUnitData in pairs(DetectedItemSet:GetSet())do +local DetectedUnit=DetectedUnitData +local DetectedObject=nil +if DetectedUnit:IsAlive()then +DetectedObject=self:GetDetectedObject(DetectedUnit:GetName()) +end +if DetectedObject then +self:IdentifyDetectedObject(DetectedObject) +DetectedItem.TypeName=DetectedUnit:GetTypeName() +DetectedItem.CategoryName=DetectedUnit:GetCategoryName() +DetectedItem.Name=DetectedObject.Name +DetectedItem.IsVisible=DetectedObject.IsVisible +DetectedItem.LastTime=DetectedObject.LastTime +DetectedItem.LastPos=DetectedObject.LastPos +DetectedItem.LastVelocity=DetectedObject.LastVelocity +DetectedItem.KnowType=DetectedObject.KnowType +DetectedItem.KnowDistance=DetectedObject.KnowDistance +DetectedItem.Distance=DetectedObject.Distance +else +self:AddChangeUnit(DetectedItem,"RU",DetectedUnitName) +DetectedItemSet:Remove(DetectedUnitName) +end +end +end +for DetectedUnitName,DetectedObjectData in pairs(self.DetectedObjects)do +local DetectedObject=self:GetDetectedObject(DetectedUnitName) +if DetectedObject then +self:T({"Detected Unit #",DetectedUnitName}) +local DetectedUnit=UNIT:FindByName(DetectedUnitName) +if DetectedUnit then +local DetectedTypeName=DetectedUnit:GetTypeName() +local DetectedItem=self:GetDetectedItem(DetectedUnitName) +if not DetectedItem then +self:T("Added new DetectedItem") +DetectedItem=self:AddDetectedItem("UNIT",DetectedUnitName) +DetectedItem.TypeName=DetectedUnit:GetTypeName() +DetectedItem.Name=DetectedObject.Name +DetectedItem.IsVisible=DetectedObject.IsVisible +DetectedItem.LastTime=DetectedObject.LastTime +DetectedItem.LastPos=DetectedObject.LastPos +DetectedItem.LastVelocity=DetectedObject.LastVelocity +DetectedItem.KnowType=DetectedObject.KnowType +DetectedItem.KnowDistance=DetectedObject.KnowDistance +DetectedItem.Distance=DetectedObject.Distance +end +DetectedItem.Set:AddUnit(DetectedUnit) +self:AddChangeUnit(DetectedItem,"AU",DetectedTypeName) +end +end +end +for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do +local DetectedItem=DetectedItemData +local DetectedSet=DetectedItem.Set +local DetectedFirstUnit=DetectedSet:GetFirst() +local DetectedFirstUnitCoord=DetectedFirstUnit:GetCoordinate() +self:SetDetectedItemCoordinate(DetectedItem,DetectedFirstUnitCoord,DetectedFirstUnit) +self:ReportFriendliesNearBy({DetectedItem=DetectedItem,ReportSetGroup=self.DetectionSetGroup}) +end +end +function DETECTION_UNITS:DetectedItemMenu(Index,AttackGroup) +self:F(Index) +local DetectedItem=self:GetDetectedItem(Index) +local DetectedSet=self:GetDetectedSet(Index) +local DetectedItemID=self:GetDetectedItemID(Index) +self:T(DetectedSet) +if DetectedSet then +local ReportSummary="" +local UnitDistanceText="" +local UnitCategoryText="" +local DetectedItemCoordinate=self:GetDetectedItemCoordinate(Index) +local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup) +ReportSummary=string.format( +"%s - %s", +DetectedItemID, +DetectedItemCoordText +) +self:T(ReportSummary) +return ReportSummary +end +end +function DETECTION_UNITS:DetectedItemReportSummary(Index,AttackGroup,Settings) +self:F({Index,self.DetectedItems}) +local DetectedItem=self:GetDetectedItem(Index) +local DetectedItemID=self:GetDetectedItemID(Index) +if DetectedItem then +local ReportSummary="" +local UnitDistanceText="" +local UnitCategoryText="" +if DetectedItem.KnowType then +local UnitCategoryName=DetectedItem.CategoryName +if UnitCategoryName then +UnitCategoryText=UnitCategoryName +end +if DetectedItem.TypeName then +UnitCategoryText=UnitCategoryText.." ("..DetectedItem.TypeName..")" +end +else +UnitCategoryText="Unknown" +end +if DetectedItem.KnowDistance then +if DetectedItem.IsVisible then +UnitDistanceText=" at "..string.format("%.2f",DetectedItem.Distance).." km" +end +else +if DetectedItem.IsVisible then +UnitDistanceText=" at +/- "..string.format("%.0f",DetectedItem.Distance).." km" +end +end +local DetectedItemCoordinate=self:GetDetectedItemCoordinate(Index) +local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup,Settings) +local ThreatLevelA2G=self:GetDetectedItemThreatLevel(Index) +local Report=REPORT:New() +Report:Add(DetectedItemID..", "..DetectedItemCoordText) +Report:Add(string.format("Threat: [%s]",string.rep("■",ThreatLevelA2G))) +Report:Add(string.format("Type: %s%s",UnitCategoryText,UnitDistanceText)) +return Report +end +return nil +end +function DETECTION_UNITS:DetectedReportDetailed(AttackGroup) +self:F() +local Report=REPORT:New() +for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do +local DetectedItem=DetectedItem +local ReportSummary=self:DetectedItemReportSummary(DetectedItemID,AttackGroup) +Report:SetTitle("Detected units:") +Report:Add(ReportSummary:Text()) +end +local ReportText=Report:Text() +return ReportText +end +end +do +DETECTION_TYPES={ +ClassName="DETECTION_TYPES", +DetectionRange=nil, +} +function DETECTION_TYPES:New(DetectionSetGroup) +local self=BASE:Inherit(self,DETECTION_BASE:New(DetectionSetGroup)) +self._SmokeDetectedUnits=false +self._FlareDetectedUnits=false +self._SmokeDetectedZones=false +self._FlareDetectedZones=false +self._BoundDetectedZones=false +return self +end +function DETECTION_TYPES:GetChangeText(DetectedItem) +self:F(DetectedItem) +local MT={} +for ChangeCode,ChangeData in pairs(DetectedItem.Changes)do +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]=" New target(s) detected: "..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]=" Invisible or destroyed target(s): "..table.concat(MTUT,", ").."." +end +end +return table.concat(MT,"\n") +end +function DETECTION_TYPES:CreateDetectionItems() +self:F2(#self.DetectedObjects) +for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do +local DetectedItemSet=DetectedItem.Set +local DetectedTypeName=DetectedItem.TypeName +for DetectedUnitName,DetectedUnitData in pairs(DetectedItemSet:GetSet())do +local DetectedUnit=DetectedUnitData +local DetectedObject=nil +if DetectedUnit:IsAlive()then +DetectedObject=self:GetDetectedObject(DetectedUnit:GetName()) +end +if DetectedObject then +self:IdentifyDetectedObject(DetectedObject) +else +self:AddChangeUnit(DetectedItem,"RU",DetectedUnitName) +DetectedItemSet:Remove(DetectedUnitName) +end +end +end +for DetectedUnitName,DetectedObjectData in pairs(self.DetectedObjects)do +local DetectedObject=self:GetDetectedObject(DetectedUnitName) +if DetectedObject then +self:T({"Detected Unit #",DetectedUnitName}) +local DetectedUnit=UNIT:FindByName(DetectedUnitName) +if DetectedUnit then +local DetectedTypeName=DetectedUnit:GetTypeName() +local DetectedItem=self:GetDetectedItem(DetectedTypeName) +if not DetectedItem then +DetectedItem=self:AddDetectedItem("TYPE",DetectedTypeName) +DetectedItem.TypeName=DetectedUnit:GetTypeName() +end +DetectedItem.Set:AddUnit(DetectedUnit) +self:AddChangeUnit(DetectedItem,"AU",DetectedTypeName) +end +end +end +for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do +local DetectedItem=DetectedItemData +local DetectedSet=DetectedItem.Set +local DetectedFirstUnit=DetectedSet:GetFirst() +local DetectedUnitCoord=DetectedFirstUnit:GetCoordinate() +self:SetDetectedItemCoordinate(DetectedItem,DetectedUnitCoord,DetectedFirstUnit) +self:ReportFriendliesNearBy({DetectedItem=DetectedItem,ReportSetGroup=self.DetectionSetGroup}) +end +end +function DETECTION_TYPES:DetectedItemMenu(DetectedTypeName,AttackGroup) +self:F(DetectedTypeName) +local DetectedItem=self:GetDetectedItem(DetectedTypeName) +local DetectedItemID=self:GetDetectedItemID(DetectedTypeName) +if DetectedItem then +local DetectedItemCoordinate=self:GetDetectedItemCoordinate(DetectedTypeName) +local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup) +local ReportSummary=string.format( +"%s - %s", +DetectedItemID, +DetectedItemCoordText +) +self:T(ReportSummary) +return ReportSummary +end +end +function DETECTION_TYPES:DetectedItemReportSummary(DetectedTypeName,AttackGroup,Settings) +self:F(DetectedTypeName) +local DetectedItem=self:GetDetectedItem(DetectedTypeName) +local DetectedSet=self:GetDetectedSet(DetectedTypeName) +local DetectedItemID=self:GetDetectedItemID(DetectedTypeName) +self:T(DetectedItem) +if DetectedItem then +local ThreatLevelA2G=self:GetDetectedItemThreatLevel(DetectedTypeName) +local DetectedItemsCount=DetectedSet:Count() +local DetectedItemType=DetectedItem.TypeName +local DetectedItemCoordinate=self:GetDetectedItemCoordinate(DetectedTypeName) +local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup,Settings) +local Report=REPORT:New() +Report:Add(DetectedItemID..", "..DetectedItemCoordText) +Report:Add(string.format("Threat: [%s]",string.rep("■",ThreatLevelA2G))) +Report:Add(string.format("Type: %2d of %s",DetectedItemsCount,DetectedItemType)) +return Report +end +end +function DETECTION_TYPES:DetectedReportDetailed(AttackGroup) +self:F() +local Report=REPORT:New() +for DetectedItemTypeName,DetectedItem in pairs(self.DetectedItems)do +local DetectedItem=DetectedItem +local ReportSummary=self:DetectedItemReportSummary(DetectedItemTypeName,AttackGroup) +Report:SetTitle("Detected types:") +Report:Add(ReportSummary:Text()) +end +local ReportText=Report:Text() +return ReportText +end +end +do +DETECTION_AREAS={ +ClassName="DETECTION_AREAS", +DetectionZoneRange=nil, +} +function DETECTION_AREAS:New(DetectionSetGroup,DetectionZoneRange) +local self=BASE:Inherit(self,DETECTION_BASE:New(DetectionSetGroup)) +self.DetectionZoneRange=DetectionZoneRange +self._SmokeDetectedUnits=false +self._FlareDetectedUnits=false +self._SmokeDetectedZones=false +self._FlareDetectedZones=false +self._BoundDetectedZones=false +return self +end +function DETECTION_AREAS:DetectedItemMenu(Index,AttackGroup) +self:F(Index) +local DetectedItem=self:GetDetectedItem(Index) +local DetectedItemID=self:GetDetectedItemID(Index) +if DetectedItem then +local DetectedSet=self:GetDetectedSet(Index) +local ReportSummaryItem +local DetectedZone=self:GetDetectedItemZone(Index) +local DetectedItemCoordinate=DetectedZone:GetCoordinate() +local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup) +local ReportSummary=string.format( +"%s - %s", +DetectedItemID, +DetectedItemCoordText +) +return ReportSummary +end +return nil +end +function DETECTION_AREAS:DetectedItemReportSummary(Index,AttackGroup,Settings) +self:F(Index) +local DetectedItem=self:GetDetectedItem(Index) +local DetectedItemID=self:GetDetectedItemID(Index) +if DetectedItem then +local DetectedSet=self:GetDetectedSet(Index) +local ReportSummaryItem +local DetectedZone=self:GetDetectedItemZone(Index) +local DetectedItemCoordinate=DetectedZone:GetCoordinate() +local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup,Settings) +local ThreatLevelA2G=self:GetDetectedItemThreatLevel(Index) +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))) +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(DetectedItemIndex,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:NearestFAC(DetectedItem) +local NearestRecce=nil +local DistanceRecce=1000000000 +for RecceGroupName,RecceGroup in pairs(self.DetectionSetGroup: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.Index)) +if Distance=timer.getTime()))then +TargetSetUnit:ForEachUnitPerThreatLevel(10,0, +function(TargetUnit) +self:F({TargetUnit=TargetUnit:GetName()}) +if MarkingCountFLmax then +FLmin=FLmax*0.75 +end +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*0.9) +if self.FLminuser then +FLmin=self.FLminuser +end +if self.FLmaxuser then +FLmax=self.FLmaxuser +end +if self.aircraft.FLcruiseFLmax then +self.aircraft.FLcruise=FLmax +end +local FLcruise=self:_Random_Gaussian(self.aircraft.FLcruise,(FLmax-FLmin)/4,FLmin,FLmax) +if self.FLuser then +FLcruise=self.FLuser +end +local h_climb=FLcruise-H_departure +local d_climb=h_climb/math.tan(PhiClimb) +local h_descent=FLcruise-(H_holding+h_holding) +local d_descent=h_descent/math.tan(PhiDescent) +local d_cruise=d_total-d_climb-d_descent +local text=string.format("\n******************************************************\n") +text=text..string.format("Template = %s\n\n",self.SpawnTemplatePrefix) +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.1f Deg\n",math.deg(AlphaClimb)) +text=text..string.format("Alpha descent = %6.1f Deg\n",math.deg(AlphaDescent)) +text=text..string.format("Phi (slope) = %6.1f Deg\n",math.deg(phi)) +text=text..string.format("Phi climb = %6.1f Deg\n",math.deg(PhiClimb)) +text=text..string.format("Phi descent = %6.1f Deg\n",math.deg(PhiDescent)) +text=text..string.format("Heading = %6.1f Deg\n",heading) +text=text..string.format("******************************************************\n") +env.info(RAT.id..text) +if d_cruise<0 then +d_cruise=100 +end +local c0=Pdeparture +local c1=c0:Translate(d_climb/2,heading) +local c2=c1:Translate(d_climb/2,heading) +local c3=c2:Translate(d_cruise,heading) +local c4=c3:Translate(d_descent/2,heading) +local c5=Pholding +local c6=Pdestination +local wp0=self:_Waypoint(takeoff,c0,VxClimb,H_departure,departure) +local wp1=self:_Waypoint(RAT.wp.climb,c1,VxClimb,H_departure+(FLcruise-H_departure)/2) +local wp2=self:_Waypoint(RAT.wp.cruise,c2,VxCruise,FLcruise) +local wp3=self:_Waypoint(RAT.wp.cruise,c3,VxCruise,FLcruise) +local wp4=self:_Waypoint(RAT.wp.descent,c4,VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2) +local wp5=self:_Waypoint(RAT.wp.holding,c5,VxHolding,H_holding+h_holding) +local wp6=self:_Waypoint(RAT.wp.landing,c6,VxFinal,H_destination,destination) +local waypoints={wp0,wp1,wp2,wp3,wp4,wp5,wp6} +if self.placemarkers then +self:_PlaceMarkers(waypoints) +end +self:_Routeinfo(waypoints,"Waypoint info in set_route:") +return departure,destination,waypoints +end +function RAT:_PickDeparture(takeoff) +local departures={} +if takeoff==RAT.wp.air then +if self.random_departure then +for _,airport in pairs(self.airports)do +if not self:_Excluded(airport:GetName())then +table.insert(departures,airport:GetZone()) +end +end +else +for _,name in pairs(self.departure_zones)do +if not self:_Excluded(name)then +table.insert(departures,ZONE:New(name)) +end +end +for _,name in pairs(self.departure_ports)do +if not self:_Excluded(name)then +table.insert(departures,AIRBASE:FindByName(name):GetZone()) +end +end +end +else +if self.random_departure then +for _,airport in pairs(self.airports)do +if not self:_Excluded(airport:GetName())then +table.insert(departures,airport) +end +end +else +for _,name in pairs(self.departure_ports)do +if not self:_Excluded(name)then +table.insert(departures,AIRBASE:FindByName(name)) +end +end +end +end +local departure=departures[math.random(#departures)] +local text +if departure and departure:GetName()then +if takeoff==RAT.wp.air then +text="Chosen departure zone: "..departure:GetName() +else +text="Chosen departure airport: "..departure:GetName().." (ID "..departure:GetID()..")" +end +env.info(RAT.id..text) +if self.debug then +MESSAGE:New(text,30):ToAll() +end +else +departure=nil +end +return departure +end +function RAT:_PickDestination(destinations,_random) +local destination=nil +if destinations and#destinations>0 then +destination=destinations[math.random(#destinations)] +local text="Chosen destination airport: "..destination:GetName().." (ID "..destination:GetID()..")" +env.info(RAT.id..text) +if self.debug then +MESSAGE:New(text,30):ToAll() +end +else +env.error(RAT.id.."No destination airport found.") +end +return destination +end +function RAT:_GetDestinations(departure,q,minrange,maxrange) +minrange=minrange or self.mindist +maxrange=maxrange or self.maxdist +local possible_destinations={} +if self.random_destination then +for _,airport in pairs(self.airports)do +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 +table.insert(possible_destinations,airport) +end +end +end +else +for _,name in pairs(self.destination_ports)do +if name~=departure:GetName()then +local airport=AIRBASE:FindByName(name) +table.insert(possible_destinations,airport) +end +end +end +env.info(RAT.id.."Number of possible destination airports = "..#possible_destinations) +if#possible_destinations>0 then +local function compare(a,b) +local qa=q:Get2DDistance(a:GetCoordinate()) +local qb=q:Get2DDistance(b:GetCoordinate()) +return qaself.Tinactive then +if Dg<50 then +stationary=true +end +self.ratcraft[i]["Tlastcheck"]=Tnow +self.ratcraft[i]["Pground"]=coords +end +else +self.ratcraft[i]["Tground"]=Tnow +self.ratcraft[i]["Tlastcheck"]=Tnow +self.ratcraft[i]["Pground"]=coords +end +end +local Pn=coords +local Dtravel=Pn:Get2DDistance(self.ratcraft[i]["Pnow"]) +self.ratcraft[i]["Pnow"]=Pn +self.ratcraft[i]["Distance"]=self.ratcraft[i]["Distance"]+Dtravel +local Ddestination=Pn:Get2DDistance(self.ratcraft[i].destination:GetCoordinate()) +local Hp=COORDINATE:New(self.ratcraft[i].waypoints[6].x,self.ratcraft[i].waypoints[6].alt,self.ratcraft[i].waypoints[6].y) +local Dholding=Pn:Get2DDistance(Hp) +local status=self.ratcraft[i].status +local DRholding +if self.category==RAT.cat.plane then +DRholding=8000 +else +DRholding=2000 +end +if self.ATCswitch and Dholding<=DRholding and string.match(status,"On journey")then +RAT:_ATCRegisterFlight(group:GetName(),Tnow) +self.ratcraft[i].status="Holding" +end +if(forID and i==forID)or(not forID)then +local text=string.format("ID %i of group %s\n",i,prefix) +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",self.ratcraft[i].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\n",alt/RAT.unit.FL2m,alt) +text=text..string.format("Distance travelled = %6.1f km\n",self.ratcraft[i]["Distance"]/1000) +text=text..string.format("Distance to destination = %6.1f km",Dholding/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 +if self.debug then +env.info(RAT.id..text) +end +if self.reportstatus or 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 %4.0f seconds inaktive on ground.",self.SpawnTemplatePrefix,dTlast) +env.info(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.SpawnTemplatePrefix,life) +self:_Despawn(group) +end +end +end +else +if self.debug then +local text=string.format("Group %i does not exist.",i) +env.info(RAT.id..text) +end +end +end +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 +if self.debug then +env.error(RAT.id.."Unit does not exist in RAT_Getlife(). Returning zero.") +end +end +else +if self.debug then +env.error(RAT.id.."Group does not exist in RAT_Getlife(). Returning zero.") +end +end +return life +end +function RAT:_SetStatus(group,status) +local index=self:GetSpawnIndexFromGroup(group) +env.info(RAT.id.."Status for group "..group:GetName()..": "..status) +self.ratcraft[index].status=status +end +function RAT:_OnBirth(EventData) +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." +env.info(RAT.id..text) +local status +if SpawnGroup:InAir()then +status="Just born (after air start)" +else +status="Starting engines (after birth)" +end +self:_SetStatus(SpawnGroup,status) +end +end +else +if self.debug then +env.error("Group does not exist in RAT:_OnBirthDay().") +end +end +end +function RAT:_EngineStartup(EventData) +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().." started engines." +env.info(RAT.id..text) +local status +if SpawnGroup:InAir()then +status="On journey (after air start)" +else +status="Taxiing (after engines started)" +end +self:_SetStatus(SpawnGroup,status) +end +end +else +if self.debug then +env.error("Group does not exist in RAT:_EngineStartup().") +end +end +end +function RAT:_OnTakeoff(EventData) +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().." is airborne." +env.info(RAT.id..text) +self:_SetStatus(SpawnGroup,"On journey (after takeoff)") +end +end +else +if self.debug then +env.error("Group does not exist in RAT:_OnTakeoff().") +end +end +end +function RAT:_OnLand(EventData) +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().." landed." +env.info(RAT.id..text) +self:_SetStatus(SpawnGroup,"Taxiing (after landing)") +if self.ATCswitch then +RAT:_ATCFlightLanded(SpawnGroup:GetName()) +end +if self.respawn_at_landing then +text="Event: Group "..SpawnGroup:GetName().." will be respawned." +env.info(RAT.id..text) +self:_Respawn(SpawnGroup) +end +end +end +else +if self.debug then +env.error("Group does not exist in RAT:_OnLand().") +end +end +end +function RAT:_OnEngineShutdown(EventData) +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().." shut down its engines." +env.info(RAT.id..text) +self:_SetStatus(SpawnGroup,"Parking (shutting down engines)") +if not self.respawn_at_landing then +text="Event: Group "..SpawnGroup:GetName().." will be respawned." +env.info(RAT.id..text) +self:_Respawn(SpawnGroup) +end +text="Event: Group "..SpawnGroup:GetName().." will be destroyed now." +env.info(RAT.id..text) +self:_Despawn(SpawnGroup) +end +end +else +if self.debug then +env.error("Group does not exist in RAT:_OnEngineShutdown().") +end +end +end +function RAT:_OnDead(EventData) +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().." died." +env.info(RAT.id..text) +self:_SetStatus(SpawnGroup,"Destroyed (after dead)") +end +end +else +if self.debug then +env.error("Group does not exist in RAT:_OnDead().") +end +end +end +function RAT:_OnCrash(EventData) +local SpawnGroup=EventData.IniGroup +env.info(string.format("%sGroup %s crashed!",RAT.id,SpawnGroup:GetName())) +if SpawnGroup then +local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup) +if EventPrefix then +if EventPrefix==self.alias then +local text="Event: Group "..SpawnGroup:GetName().." crashed." +env.info(RAT.id..text) +self:_SetStatus(SpawnGroup,"Crashed") +end +end +else +if self.debug then +env.error("Group does not exist in RAT:_OnCrash().") +end +end +end +function RAT:_Despawn(group) +local index=self:GetSpawnIndexFromGroup(group) +self.ratcraft[index].group:Destroy() +self.ratcraft[index].group=nil +self.alive=self.alive-1 +if self.f10menu then +self.Menu[self.SubMenuName]["groups"][index]:Remove() +end +end +function RAT:_Waypoint(Type,Coord,Speed,Altitude,Airport) +local _Altitude=Altitude or Coord.y +local Hland=Coord:GetLandHeight() +local _Type=nil +local _Action=nil +local _alttype="RADIO" +local _AID=nil +if Type==RAT.wp.cold then +_Type="TakeOffParking" +_Action="From Parking Area" +_Altitude=0 +_alttype="RADIO" +_AID=Airport:GetID() +elseif Type==RAT.wp.hot then +_Type="TakeOffParkingHot" +_Action="From Parking Area Hot" +_Altitude=0 +_alttype="RADIO" +_AID=Airport:GetID() +elseif Type==RAT.wp.runway then +_Type="TakeOff" +_Action="From Parking Area" +_Altitude=0 +_alttype="RADIO" +_AID=Airport:GetID() +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=0 +_alttype="RADIO" +_AID=Airport:GetID() +else +env.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("Template = %s\n",self.SpawnTemplatePrefix) +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 with ID %i\n",Airport:GetName(),Airport:GetID()) +end +else +text=text..string.format("No airport/zone specified\n") +end +text=text.."******************************************************\n" +if self.debug then +env.info(RAT.id..text) +end +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="RAT waypoint" +if(Airport~=nil)and Type~=RAT.wp.air then +local AirbaseID=Airport:GetID() +local AirbaseCategory=Airport:GetDesc().category +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 +end +end +RoutePoint.properties={ +["vnav"]=1, +["scale"]=0, +["angle"]=0, +["vangle"]=0, +["steer"]=2, +} +if Type==RAT.wp.holding then +local Duration=self:_Randomize(90,0.9) +RoutePoint.task=self:_TaskHolding({x=Coord.x,y=Coord.z},Altitude,Speed,Duration) +else +RoutePoint.task={} +RoutePoint.task.id="ComboTask" +RoutePoint.task.params={} +RoutePoint.task.params.tasks={} +end +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\n",i-1,p.x/1000,p.y/1000,p.alt) +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 = %i.\n",i-1,i,d/1000,heading) +end +text=text..string.format("Total distance = %6.1f km\n",total/1000) +local text=string.format("******************************************************\n") +if self.debug then +env.info(RAT.id..text) +end +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) +DCSTask.params.stopCondition={userFlag=userflagname,userFlagValue=1,duration=1800} +else +DCSTask.params.stopCondition={duration=Duration} +end +return DCSTask +end +function RAT:_FLmax(alpha,beta,d,phi,h0) +local gamma=math.rad(180)-alpha-beta +local a=d*math.sin(alpha)/math.sin(gamma) +local b=d*math.sin(beta)/math.sin(gamma) +local h1=b*math.sin(alpha) +local h2=a*math.sin(beta) +local h3=b*math.cos(math.pi/2-(alpha+phi)) +local text=string.format("\nFLmax = FL%3.0f = %6.1f m.\n",h1/RAT.unit.FL2m,h1) +text=text..string.format("FLmax = FL%3.0f = %6.1f m.\n",h2/RAT.unit.FL2m,h2) +text=text..string.format("FLmax = FL%3.0f = %6.1f m.",h3/RAT.unit.FL2m,h3) +if self.debug then +env.info(RAT.id..text) +end +return h3+h0 +end +function RAT:_MinDistance(alpha,beta,h) +local d1=h/math.tan(alpha) +local d2=h/math.tan(beta) +return d1+d2 +end +function RAT:_AirportExists(name) +for _,airport in pairs(self.airports_map)do +if airport:GetName()==name then +return true +end +end +return false +end +function RAT:_SetROE(group,roe) +env.info(RAT.id.."Setting ROE to "..roe.." for group "..group:GetName()) +if self.roe==RAT.ROE.returnfire then +group:OptionROEReturnFire() +elseif self.roe==RAT.ROE.weaponfree then +group:OptionROEWeaponFree() +else +group:OptionROEHoldFire() +end +end +function RAT:_SetROT(group,rot) +env.info(RAT.id.."Setting ROT to "..rot.." for group "..group:GetName()) +if self.rot==RAT.ROT.passive then +group:OptionROTPassiveDefense() +elseif self.rot==RAT.ROT.evade then +group:OptionROTEvadeFire() +else +group:OptionROTNoReaction() +end +end +function RAT:_SetCoalitionTable() +if self.friendly==RAT.coal.neutral then +self.ctable={coalition.side.NEUTRAL} +elseif self.friendly==RAT.coal.same then +self.ctable={self.coalition,coalition.side.NEUTRAL} +elseif self.friendly==RAT.coal.sameonly then +self.ctable={self.coalition} +else +env.error("Unknown friendly coalition in _SetCoalitionTable(). Defaulting to NEUTRAL.") +self.ctable={self.coalition,coalition.side.NEUTRAL} +end +end +function RAT:_Course(a,b) +local dx=b.x-a.x +local ay +if a.alt then +ay=a.y +else +ay=a.z +end +local by +if b.alt then +by=b.y +else +by=b.z +end +local dy=by-ay +local angle=math.deg(math.atan2(dy,dx)) +if angle<0 then +angle=360+angle +end +return angle +end +function RAT:_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) +if self.debug then +local text=string.format("Random: value = %6.2f, fac = %4.2f, min = %6.2f, max = %6.2f, r = %6.2f",value,fac,min,max,r) +env.info(RAT.id..text) +end +return r +end +function RAT:_Random_Gaussian(x0,sigma,xmin,xmax) +sigma=sigma or 10 +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 RAT:_PlaceMarkers(waypoints) +self:_SetMarker("Takeoff",waypoints[1]) +self:_SetMarker("Climb",waypoints[2]) +self:_SetMarker("Begin of Cruise",waypoints[3]) +self:_SetMarker("End of Cruise",waypoints[4]) +self:_SetMarker("Descent",waypoints[5]) +self:_SetMarker("Holding Point",waypoints[6]) +self:_SetMarker("Destination",waypoints[7]) +end +function RAT:_SetMarker(text,wp) +RAT.markerid=RAT.markerid+1 +self.markerids[#self.markerids+1]=RAT.markerid +if self.debug then +env.info(RAT.id..self.SpawnTemplatePrefix..": placing marker with ID "..RAT.markerid..": "..text) +end +local vec={x=wp.x,y=wp.alt,z=wp.y} +local text1=string.format("%s:\n%s",self.alias,text) +trigger.action.markToAll(RAT.markerid,text1,vec) +end +function RAT:_DeleteMarkers() +for k,v in ipairs(self.markerids)do +trigger.action.removeMark(v) +end +for k,v in ipairs(self.markerids)do +self.markerids[k]=nil +end +end +function RAT:_ModifySpawnTemplate(waypoints) +local PointVec3={x=waypoints[1].x,y=waypoints[1].alt,z=waypoints[1].y} +local heading=self:_Course(waypoints[1],waypoints[2]) +if self:_GetSpawnIndex(self.SpawnIndex+1)then +local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate +if SpawnTemplate then +self:T(SpawnTemplate) +for UnitID=1,#SpawnTemplate.units do +self:T('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) +SpawnTemplate.units[UnitID].x=TX +SpawnTemplate.units[UnitID].y=TY +SpawnTemplate.units[UnitID].alt=PointVec3.y +SpawnTemplate.units[UnitID].heading=math.rad(heading) +if self.livery then +SpawnTemplate.units[UnitID].livery_id=self.livery[math.random(#self.livery)] +end +SpawnTemplate.units[UnitID]["skill"]=self.skill +SpawnTemplate.units[UnitID]["onboard_num"]=self.SpawnIndex +SpawnTemplate.CoalitionID=self.coalition +if self.country then +SpawnTemplate.CountryID=self.country +end +UnitTemplate.parking=nil +UnitTemplate.parking_id=nil +UnitTemplate.alt=PointVec3.y +self:T('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) +end +for i,wp in ipairs(waypoints)do +SpawnTemplate.route.points[i]=wp +end +SpawnTemplate.x=PointVec3.x +SpawnTemplate.y=PointVec3.z +self.SpawnGroups[self.SpawnIndex].SpawnTemplate=SpawnTemplate +self:T(SpawnTemplate) +end +end +end +function RAT:_ATCInit(airports_map) +if not RAT.ATC.init then +env.info(RAT.id.."Starting RAT ATC.") +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=nil +RAT.ATC.airport[name].traffic=0 +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) +env.info(string.format("%s%s ATC: 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) +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 is currently clear" +if RAT.ATC.airport[dest].busy then +if RAT.ATC.airport[dest].onfinal then +busy="Runway is occupied by "..RAT.ATC.airport[dest].onfinal +else +busy="Runway is occupied" +end +end +env.info(string.format("%s%s ATC: Flight %s is holding for %i:%02d. %s.",RAT.id,dest,name,hold/60,hold%60,busy)) +elseif hold==RAT.ATC.onfinal then +local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal +env.info(string.format("%s%s ATC: Flight %s was cleared for landing. Waiting %i:%02d for landing event.",RAT.id,dest,name,Tfinal/60,Tfinal%60)) +if Tfinal>300 then +end +elseif hold==RAT.ATC.unregistered then +else +env.error(RAT.id.."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 +local qw={} +for qID,flight in ipairs(RAT.ATC.airport[name].queue)do +local nqueue=#RAT.ATC.airport[name].queue +if RAT.ATC.airport[name].busy then +RAT.ATC.flight[flight].holding=Tnow-RAT.ATC.flight[flight].Tarrive +local text=string.format("%s ATC: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.",name,flight,qID,nqueue,RAT.ATC.flight[flight].holding/60,RAT.ATC.flight[flight].holding%60) +env.info(text) +else +RAT:_ATCClearForLanding(name,flight) +table.insert(qw,qID) +end +end +for _,i in pairs(qw)do +table.remove(RAT.ATC.airport[name].queue,i) +end +end +end +function RAT:_ATCClearForLanding(airport,flight) +RAT.ATC.flight[flight].holding=RAT.ATC.onfinal +RAT.ATC.airport[airport].busy=true +RAT.ATC.airport[airport].onfinal=flight +RAT.ATC.flight[flight].Tonfinal=timer.getTime() +trigger.action.setUserFlag(flight,1) +local flagvalue=trigger.misc.getUserFlag(flight) +local text1=string.format("%s%s ATC: Flight %s cleared for final approach (flag=%d).",RAT.id,airport,flight,flagvalue) +local text2=string.format("%s ATC: Flight %s you are cleared for landing.",airport,flight) +env.info(text1) +MESSAGE:New(text2,10):ToAll() +end +function RAT:_ATCFlightLanded(name) +if RAT.ATC.flight[name]then +local dest=RAT.ATC.flight[name].destination +local Tnow=timer.getTime() +local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal +local Thold=RAT.ATC.flight[name].Tonfinal-RAT.ATC.flight[name].Tarrive +RAT.ATC.airport[dest].busy=false +RAT.ATC.airport[dest].onfinal=nil +RAT:_ATCDelFlight(RAT.ATC.flight,name) +RAT.ATC.airport[dest].traffic=RAT.ATC.airport[dest].traffic+1 +local text1=string.format("%s%s ATC: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.",RAT.id,dest,name,Thold/60,Thold%60,Tfinal/60,Tfinal%60) +local text2=string.format("%s ATC: Flight %s landed. Welcome to %s.",dest,name,dest) +env.info(text1) +env.info(string.format("%s%s ATC: Number of planes landed in total %d.",RAT.id,dest,RAT.ATC.airport[dest].traffic)) +MESSAGE:New(text2,10):ToAll() +end +end +function RAT:_ATCQueue() +for airport,_ in pairs(RAT.ATC.airport)do +local _queue={} +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 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 +PROTECT={ +ClassName="PROTECT", +} +function PROTECT:New(ProtectZone,Coalition) +local self=BASE:Inherit(self,FSM:New()) +self.ProtectZone=ProtectZone +self.ProtectUnitSet=SET_UNIT:New() +self.ProtectStaticSet=SET_STATIC:New() +self.CaptureUnitSet=SET_UNIT:New() +self:SetStartState("-") +self:AddTransition({"-","Protected","Captured"},"Protected","Protected") +self:AddTransition({"Protected","Attacked"},"Destroyed","Destroyed") +self:AddTransition({"Protected","Destroyed"},"Attacked","Attacked") +self:AddTransition({"Protected","Attacked","Destroyed"},"Captured","Captured") +self:ScheduleRepeat(60,60,0,nil,self.Status,self) +self:SetCoalition(Coalition) +self:__Protected(5) +return self +end +function PROTECT:SetCoalition(Coalition) +self.Coalition=Coalition +end +function PROTECT:GetCoalition() +return self.Coalition +end +function PROTECT:AddProtectUnit(ProtectUnit) +self.ProtectUnitSet:AddUnit(ProtectUnit) +end +function PROTECT:GetProtectUnitSet() +return self.ProtectUnitSet +end +function PROTECT:AddProtectStatic(ProtectStatic) +self.ProtectStaticSet:AddStatic(ProtectStatic) +end +function PROTECT:GetProtectStaticSet() +return self.ProtectStaticSet +end +function PROTECT:AddCaptureUnit(CaptureUnit) +self.CaptureUnitSet:AddUnit(CaptureUnit) +end +function PROTECT:GetCaptureUnitSet() +return self.CaptureUnitSet +end +function PROTECT:IsProtected() +local IsAllCoalition=self.ProtectZone:IsAllInZoneOfCoalition(self.Coalition) +self:E({IsAllCoalition=IsAllCoalition}) +return IsAllCoalition +end +function PROTECT:IsCaptured() +local IsCaptured=self.ProtectZone:IsAllInZoneOfOtherCoalition(self.Coalition) +self:E({IsCaptured=IsCaptured}) +return IsCaptured +end +function PROTECT:IsAttacked() +local IsSomeCoalition=self.ProtectZone:IsSomeInZoneOfCoalition(self.Coalition) +self:E({IsSomeCoalition=IsSomeCoalition}) +return IsSomeCoalition +end +function PROTECT:AreProtectUnitsAlive() +local IsAlive=false +local UnitSet=self.ProtectUnitSet +UnitSet:Flush() +local UnitList=UnitSet:GetSet() +for UnitID,ProtectUnit in pairs(UnitList)do +local IsUnitAlive=ProtectUnit:IsAlive() +if IsUnitAlive==true then +IsAlive=true +break +end +end +return IsAlive +end +function PROTECT:AreProtectStaticsAlive() +local IsAlive=false +local StaticSet=self.ProtectStaticSet +StaticSet:Flush() +local StaticList=StaticSet:GetSet() +for UnitID,ProtectStatic in pairs(StaticList)do +local IsStaticAlive=ProtectStatic:IsAlive() +if IsStaticAlive==true then +IsAlive=true +break +end +end +return IsAlive +end +function PROTECT:IsCaptureUnitInZone() +local CaptureUnitSet=self.CaptureUnitSet +CaptureUnitSet:Flush() +local IsInZone=self.CaptureUnitSet:IsPartiallyInZone(self.ProtectZone) +self:E({IsInZone=IsInZone}) +return IsInZone +end +function PROTECT:Smoke(SmokeColor) +self.ProtectZone:GetCoordinate():Smoke(SmokeColor) +end +function PROTECT:onenterCaptured() +local NewCoalition=self.ProtectZone:GetCoalition() +self:E({NewCoalition=NewCoalition}) +self:SetCoalition(NewCoalition) +end +function PROTECT:Status() +self.ProtectZone:Scan() +if self:IsProtected()then +self:Protected() +else +if self:IsAttacked()then +self:Attacked() +else +if self:IsCaptured()then +self:Captured() +end +end +end +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:E("Spawning new AIGroup") +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() +SetGroup:Remove(ClientName) +SetGroup:Flush() +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:MessageToRed("Returning to "..ClosestAirbase:GetName().." ...",30) +local RTBRoute=AIGroup:RouteReturnToAirbase(ClosestAirbase) +AIGroupTemplate.route=RTBRoute +AIGroup:Respawn(AIGroupTemplate) +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 Client:IsAlive()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:ForEachPlayer( +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.") +if not self.SpawnQueue[Client.UnitName]then +self:__Spawn(math.random(self.Earliest,self.Latest),Client.UnitName) +self.SpawnQueue[Client.UnitName]=true +self:E("New AI Spawned for Client "..Client.UnitName) +end +end +end +return true +end +) +self:__Monitor(10) +end +AI_A2A={ +ClassName="AI_A2A", +} +function AI_A2A:New(AIGroup) +local self=BASE:Inherit(self,FSM_CONTROLLABLE:New()) +self:SetControllable(AIGroup) +self:SetFuelThreshold(.2,60) +self:SetDamageThreshold(0.4) +self:SetDisengageRadius(70000) +self:SetStartState("Stopped") +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_A2A:SetDispatcher(Dispatcher) +self.Dispatcher=Dispatcher +end +function AI_A2A:GetDispatcher() +return self.Dispatcher +end +function AI_A2A: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_A2A:ClearTargetDistance() +self.TargetDistance=nil +self.ClosestTargetDistance=nil +end +function AI_A2A:SetSpeed(PatrolMinSpeed,PatrolMaxSpeed) +self:F2({PatrolMinSpeed,PatrolMaxSpeed}) +self.PatrolMinSpeed=PatrolMinSpeed +self.PatrolMaxSpeed=PatrolMaxSpeed +end +function AI_A2A:SetAltitude(PatrolFloorAltitude,PatrolCeilingAltitude) +self:F2({PatrolFloorAltitude,PatrolCeilingAltitude}) +self.PatrolFloorAltitude=PatrolFloorAltitude +self.PatrolCeilingAltitude=PatrolCeilingAltitude +end +function AI_A2A:SetHomeAirbase(HomeAirbase) +self:F2({HomeAirbase}) +self.HomeAirbase=HomeAirbase +end +function AI_A2A:SetTanker(TankerName) +self:F2({TankerName}) +self.TankerName=TankerName +end +function AI_A2A:SetDisengageRadius(DisengageRadius) +self:F2({DisengageRadius}) +self.DisengageRadius=DisengageRadius +end +function AI_A2A:SetStatusOff() +self:F2() +self.CheckStatus=false +end +function AI_A2A:SetFuelThreshold(PatrolFuelThresholdPercentage,PatrolOutOfFuelOrbitTime) +self.PatrolManageFuel=true +self.PatrolFuelThresholdPercentage=PatrolFuelThresholdPercentage +self.PatrolOutOfFuelOrbitTime=PatrolOutOfFuelOrbitTime +self.Controllable:OptionRTBBingoFuel(false) +return self +end +function AI_A2A:SetDamageThreshold(PatrolDamageThreshold) +self.PatrolManageDamage=true +self.PatrolDamageThreshold=PatrolDamageThreshold +return self +end +function AI_A2A:onafterStart(Controllable,From,Event,To) +self:F2() +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_A2A:onbeforeStatus() +return self.CheckStatus +end +function AI_A2A:onafterStatus() +self:F(" Checking Status") +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()) +self:F({DistanceFromHomeBase=DistanceFromHomeBase}) +if DistanceFromHomeBase>self.DisengageRadius then +self:E(self.Controllable:GetName().." is too far from home base, RTB!") +self:Hold(300) +RTB=false +end +end +if self:Is("Fuel")or self:Is("Damaged")or self:Is("LostControl")then +if DistanceFromHomeBase<5000 then +self:E(self.Controllable:GetName().." is too far from home base, RTB!") +self:Home("Destroy") +end +end +if not self:Is("Fuel")and not self:Is("Home")then +local Fuel=self.Controllable:GetFuel() +self:F({Fuel=Fuel}) +if Fuel=2 then +if Damage~=InitialLife then +self:Damaged() +else +self:E(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(0.5) +end +self:__Status(10) +end +end +function AI_A2A.RTBRoute(AIGroup,Fsm) +AIGroup:F({"AI_A2A.RTBRoute:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +Fsm:__RTB(0.5) +end +end +function AI_A2A.RTBHold(AIGroup,Fsm) +AIGroup:F({"AI_A2A.RTBHold:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +Fsm:__RTB(0.5) +Fsm:Return() +local Task=AIGroup:TaskOrbitCircle(4000,400) +AIGroup:SetTask(Task) +end +end +function AI_A2A:onafterRTB(AIGroup,From,Event,To) +self:F({AIGroup,From,Event,To}) +if AIGroup and AIGroup:IsAlive()then +self:E("Group "..AIGroup:GetName().." ... RTB! ( "..self:GetState().." )") +self:ClearTargetDistance() +AIGroup:ClearTasks() +local EngageRoute={} +local CurrentCoord=AIGroup:GetCoordinate() +local ToTargetCoord=self.HomeAirbase:GetCoordinate() +local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) +local ToAirbaseAngle=CurrentCoord:GetAngleDegrees(CurrentCoord:GetDirectionVec3(ToTargetCoord)) +local Distance=CurrentCoord:Get2DDistance(ToTargetCoord) +local ToAirbaseCoord=CurrentCoord:Translate(5000,ToAirbaseAngle) +if Distance<5000 then +self:E("RTB and near the airbase!") +self:Home() +return +end +local ToRTBRoutePoint=ToAirbaseCoord:WaypointAir( +self.PatrolAltType, +POINT_VEC3.RoutePointType.TurningPoint, +POINT_VEC3.RoutePointAction.TurningPoint, +ToTargetSpeed, +true +) +self:F({Angle=ToAirbaseAngle,ToTargetSpeed=ToTargetSpeed}) +self:T2({self.MinSpeed,self.MaxSpeed,ToTargetSpeed}) +EngageRoute[#EngageRoute+1]=ToRTBRoutePoint +EngageRoute[#EngageRoute+1]=ToRTBRoutePoint +AIGroup:OptionROEHoldFire() +AIGroup:OptionROTEvadeFire() +AIGroup:WayPointInitialize(EngageRoute) +local Tasks={} +Tasks[#Tasks+1]=AIGroup:TaskFunction("AI_A2A.RTBRoute",self) +EngageRoute[#EngageRoute].task=AIGroup:TaskCombo(Tasks) +AIGroup:Route(EngageRoute,0.5) +end +end +function AI_A2A:onafterHome(AIGroup,From,Event,To) +self:F({AIGroup,From,Event,To}) +self:E("Group "..self.Controllable:GetName().." ... Home! ( "..self:GetState().." )") +if AIGroup and AIGroup:IsAlive()then +end +end +function AI_A2A:onafterHold(AIGroup,From,Event,To,HoldTime) +self:F({AIGroup,From,Event,To}) +self:E("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_A2A.RTBHold",self) +local OrbitHoldTask=AIGroup:TaskOrbitCircle(4000,self.PatrolMinSpeed) +AIGroup:SetTask(AIGroup:TaskCombo({TimedOrbitTask,RTBTask,OrbitHoldTask}),1) +end +end +function AI_A2A.Resume(AIGroup,Fsm) +AIGroup:F({"AI_A2A.Resume:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +Fsm:__RTB(0.5) +end +end +function AI_A2A:onafterRefuel(AIGroup,From,Event,To) +self:F({AIGroup,From,Event,To}) +self:E("Group "..self.Controllable:GetName().." ... Refuelling! ( "..self:GetState().." )") +if AIGroup and AIGroup:IsAlive()then +local Tanker=GROUP:FindByName(self.TankerName) +if Tanker:IsAlive()and Tanker:IsAirPlane()then +local RefuelRoute={} +local CurrentCoord=AIGroup:GetCoordinate() +local ToRefuelCoord=Tanker:GetCoordinate() +local ToRefuelSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) +local ToRefuelRoutePoint=ToRefuelCoord:WaypointAir( +self.PatrolAltType, +POINT_VEC3.RoutePointType.TurningPoint, +POINT_VEC3.RoutePointAction.TurningPoint, +ToRefuelSpeed, +true +) +self:F({ToRefuelSpeed=ToRefuelSpeed}) +RefuelRoute[#RefuelRoute+1]=ToRefuelRoutePoint +RefuelRoute[#RefuelRoute+1]=ToRefuelRoutePoint +AIGroup:OptionROEHoldFire() +AIGroup:OptionROTEvadeFire() +local Tasks={} +Tasks[#Tasks+1]=AIGroup:TaskRefueling() +Tasks[#Tasks+1]=AIGroup:TaskFunction(self:GetClassName()..".Resume",self) +RefuelRoute[#RefuelRoute].task=AIGroup:TaskCombo(Tasks) +AIGroup:Route(RefuelRoute,0.5) +else +self:RTB() +end +end +end +function AI_A2A:onafterDead() +self:SetStatusOff() +end +function AI_A2A:OnCrash(EventData) +if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then +self:E(self.Controllable:GetUnits()) +if#self.Controllable:GetUnits()==1 then +self:__Crash(1,EventData) +end +end +end +function AI_A2A:OnEjection(EventData) +if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then +self:__Eject(1,EventData) +end +end +function AI_A2A:OnPilotDead(EventData) +if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then +self:__PilotDead(1,EventData) +end +end +AI_A2A_PATROL={ +ClassName="AI_A2A_PATROL", +} +function AI_A2A_PATROL:New(AIGroup,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType) +local self=BASE:Inherit(self,AI_A2A:New(AIGroup)) +self.PatrolZone=PatrolZone +self.PatrolFloorAltitude=PatrolFloorAltitude +self.PatrolCeilingAltitude=PatrolCeilingAltitude +self.PatrolMinSpeed=PatrolMinSpeed +self.PatrolMaxSpeed=PatrolMaxSpeed +self.PatrolAltType=PatrolAltType or"RADIO" +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(Controllable,From,Event,To) +self:F2() +self:ClearTargetDistance() +self:__Route(1) +self.Controllable:OnReSpawn( +function(PatrolGroup) +self:E("ReSpawn") +self:__Reset(1) +self:__Route(5) +end +) +end +function AI_A2A_PATROL.PatrolRoute(AIGroup,Fsm) +AIGroup:F({"AI_A2A_PATROL.PatrolRoute:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +Fsm:Route() +end +end +function AI_A2A_PATROL:onafterRoute(AIGroup,From,Event,To) +self:F2() +if From=="RTB"then +return +end +if AIGroup:IsAlive()then +local PatrolRoute={} +local CurrentCoord=AIGroup:GetCoordinate() +local ToTargetCoord=self.PatrolZone:GetRandomPointVec2() +ToTargetCoord:SetAlt(math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude)) +self:SetTargetDistance(ToTargetCoord) +local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed) +local ToPatrolRoutePoint=ToTargetCoord:WaypointAir( +self.PatrolAltType, +POINT_VEC3.RoutePointType.TurningPoint, +POINT_VEC3.RoutePointAction.TurningPoint, +ToTargetSpeed, +true +) +PatrolRoute[#PatrolRoute+1]=ToPatrolRoutePoint +PatrolRoute[#PatrolRoute+1]=ToPatrolRoutePoint +local Tasks={} +Tasks[#Tasks+1]=AIGroup:TaskFunction("AI_A2A_PATROL.PatrolRoute",self) +PatrolRoute[#PatrolRoute].task=AIGroup:TaskCombo(Tasks) +AIGroup:OptionROEReturnFire() +AIGroup:OptionROTPassiveDefense() +AIGroup:Route(PatrolRoute,0.5) +end +end +function AI_A2A_PATROL.Resume(AIGroup) +AIGroup:F({"AI_A2A_PATROL.Resume:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +local _AI_A2A=AIGroup:GetState(AIGroup,"AI_A2A") +_AI_A2A:__Reset(1) +_AI_A2A:__Route(5) +end +end +AI_A2A_CAP={ +ClassName="AI_A2A_CAP", +} +function AI_A2A_CAP:New(AIGroup,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,PatrolAltType) +local self=BASE:Inherit(self,AI_A2A_PATROL:New(AIGroup,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)) +self.Accomplished=false +self.Engaging=false +self.EngageMinSpeed=EngageMinSpeed +self.EngageMaxSpeed=EngageMaxSpeed +self:AddTransition({"Patrolling","Engaging","Returning","Airborne"},"Engage","Engaging") +self:AddTransition("Engaging","Fired","Engaging") +self:AddTransition("*","Destroy","*") +self:AddTransition("Engaging","Abort","Patrolling") +self:AddTransition("Engaging","Accomplish","Patrolling") +return self +end +function AI_A2A_CAP:onafterStart(AIGroup,From,Event,To) +AIGroup: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:onafterPatrol(AIGroup,From,Event,To) +self:GetParent(self).onafterPatrol(self,AIGroup,From,Event,To) +self:HandleEvent(EVENTS.Dead) +end +function AI_A2A_CAP.AttackRoute(AIGroup,Fsm) +AIGroup:F({"AI_A2A_CAP.AttackRoute:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +Fsm:__Engage(0.5) +end +end +function AI_A2A_CAP:onbeforeEngage(AIGroup,From,Event,To) +if self.Accomplished==true then +return false +end +end +function AI_A2A_CAP:onafterAbort(AIGroup,From,Event,To) +AIGroup:ClearTasks() +self:__Route(0.5) +end +function AI_A2A_CAP:onafterEngage(AIGroup,From,Event,To,AttackSetUnit) +self:F({AIGroup,From,Event,To,AttackSetUnit}) +self.AttackSetUnit=AttackSetUnit or self.AttackSetUnit +local FirstAttackUnit=self.AttackSetUnit:GetFirst() +if FirstAttackUnit and FirstAttackUnit:IsAlive()then +if AIGroup:IsAlive()then +local EngageRoute={} +local CurrentCoord=AIGroup:GetCoordinate() +local ToTargetCoord=self.AttackSetUnit:GetFirst():GetCoordinate() +local ToTargetSpeed=math.random(self.EngageMinSpeed,self.EngageMaxSpeed) +local ToInterceptAngle=CurrentCoord:GetAngleDegrees(CurrentCoord:GetDirectionVec3(ToTargetCoord)) +local ToPatrolRoutePoint=CurrentCoord:Translate(5000,ToInterceptAngle):WaypointAir( +self.PatrolAltType, +POINT_VEC3.RoutePointType.TurningPoint, +POINT_VEC3.RoutePointAction.TurningPoint, +ToTargetSpeed, +true +) +self:F({Angle=ToInterceptAngle,ToTargetSpeed=ToTargetSpeed}) +self:T2({self.MinSpeed,self.MaxSpeed,ToTargetSpeed}) +EngageRoute[#EngageRoute+1]=ToPatrolRoutePoint +EngageRoute[#EngageRoute+1]=ToPatrolRoutePoint +local AttackTasks={} +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 +AttackTasks[#AttackTasks+1]=AIGroup:TaskAttackUnit(AttackUnit) +end +end +if#AttackTasks==0 then +self:E("No targets found -> Going back to Patrolling") +self:__Abort(0.5) +else +AIGroup:OptionROEOpenFire() +AIGroup:OptionROTPassiveDefense() +AttackTasks[#AttackTasks+1]=AIGroup:TaskFunction("AI_A2A_CAP.AttackRoute",self) +EngageRoute[#EngageRoute].task=AIGroup:TaskCombo(AttackTasks) +end +AIGroup:Route(EngageRoute,0.5) +end +else +self:E("No targets found -> Going back to Patrolling") +self:__Abort(0.5) +end +end +function AI_A2A_CAP:onafterAccomplish(Controllable,From,Event,To) +self.Accomplished=true +self:SetDetectionOff() +end +function AI_A2A_CAP:onafterDestroy(Controllable,From,Event,To,EventData) +if EventData.IniUnit then +self.AttackUnits[EventData.IniUnit]=nil +end +end +function AI_A2A_CAP:OnEventDead(EventData) +self:F({"EventDead",EventData}) +if EventData.IniDCSUnit then +if self.AttackUnits and self.AttackUnits[EventData.IniUnit]then +self:__Destroy(1,EventData) +end +end +end +function AI_A2A_CAP.Resume(AIGroup) +AIGroup:F({"AI_A2A_CAP.Resume:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +local _AI_A2A=AIGroup:GetState(AIGroup,"AI_A2A") +_AI_A2A:__Reset(1) +_AI_A2A:__Route(5) +end +end +AI_A2A_GCI={ +ClassName="AI_A2A_GCI", +} +function AI_A2A_GCI:New(AIGroup,EngageMinSpeed,EngageMaxSpeed) +local self=BASE:Inherit(self,AI_A2A:New(AIGroup)) +self.Accomplished=false +self.Engaging=false +self.EngageMinSpeed=EngageMinSpeed +self.EngageMaxSpeed=EngageMaxSpeed +self.PatrolMinSpeed=EngageMinSpeed +self.PatrolMaxSpeed=EngageMaxSpeed +self.PatrolAltType="RADIO" +self:AddTransition({"Started","Engaging","Returning","Airborne"},"Engage","Engaging") +self:AddTransition("Engaging","Fired","Engaging") +self:AddTransition("*","Destroy","*") +self:AddTransition("Engaging","Abort","Patrolling") +self:AddTransition("Engaging","Accomplish","Patrolling") +return self +end +function AI_A2A_GCI:onafterStart(AIGroup,From,Event,To) +AIGroup:HandleEvent(EVENTS.Takeoff,nil,self) +end +function AI_A2A_GCI:onafterEngage(AIGroup,From,Event,To) +self:HandleEvent(EVENTS.Dead) +end +function AI_A2A_GCI.InterceptRoute(AIGroup,Fsm) +AIGroup:F({"AI_A2A_GCI.InterceptRoute:",AIGroup:GetName()}) +if AIGroup:IsAlive()then +Fsm:__Engage(0.5) +end +end +function AI_A2A_GCI:onbeforeEngage(AIGroup,From,Event,To) +if self.Accomplished==true then +return false +end +end +function AI_A2A_GCI:onafterAbort(AIGroup,From,Event,To) +AIGroup:ClearTasks() +self:Return() +self:__RTB(0.5) +end +function AI_A2A_GCI:onafterEngage(AIGroup,From,Event,To,AttackSetUnit) +self:F({AIGroup,From,Event,To,AttackSetUnit}) +self.AttackSetUnit=AttackSetUnit or self.AttackSetUnit +local FirstAttackUnit=self.AttackSetUnit:GetFirst() +if FirstAttackUnit and FirstAttackUnit:IsAlive()then +if AIGroup:IsAlive()then +local EngageRoute={} +local CurrentCoord=AIGroup:GetCoordinate() +local CurrentCoord=AIGroup:GetCoordinate() +local ToTargetCoord=self.AttackSetUnit:GetFirst():GetCoordinate() +self:SetTargetDistance(ToTargetCoord) +local ToTargetSpeed=math.random(self.EngageMinSpeed,self.EngageMaxSpeed) +local ToInterceptAngle=CurrentCoord:GetAngleDegrees(CurrentCoord:GetDirectionVec3(ToTargetCoord)) +local ToPatrolRoutePoint=CurrentCoord:Translate(15000,ToInterceptAngle):WaypointAir( +self.PatrolAltType, +POINT_VEC3.RoutePointType.TurningPoint, +POINT_VEC3.RoutePointAction.TurningPoint, +ToTargetSpeed, +true +) +self:F({Angle=ToInterceptAngle,ToTargetSpeed=ToTargetSpeed}) +self:F({self.EngageMinSpeed,self.EngageMaxSpeed,ToTargetSpeed}) +EngageRoute[#EngageRoute+1]=ToPatrolRoutePoint +EngageRoute[#EngageRoute+1]=ToPatrolRoutePoint +local AttackTasks={} +for AttackUnitID,AttackUnit in pairs(self.AttackSetUnit:GetSet())do +local AttackUnit=AttackUnit +if AttackUnit:IsAlive()and AttackUnit:IsAir()then +self:T({"Intercepting Unit:",AttackUnit:GetName(),AttackUnit:IsAlive(),AttackUnit:IsAir()}) +AttackTasks[#AttackTasks+1]=AIGroup:TaskAttackUnit(AttackUnit) +end +end +if#AttackTasks==0 then +self:E("No targets found -> Going RTB") +self:Return() +self:__RTB(0.5) +else +AIGroup:OptionROEOpenFire() +AIGroup:OptionROTPassiveDefense() +AttackTasks[#AttackTasks+1]=AIGroup:TaskFunction("AI_A2A_GCI.InterceptRoute",self) +EngageRoute[#EngageRoute].task=AIGroup:TaskCombo(AttackTasks) +end +AIGroup:Route(EngageRoute,0.5) +end +else +self:E("No targets found -> Going RTB") +self:Return() +self:__RTB(0.5) +end +end +function AI_A2A_GCI:onafterAccomplish(AIGroup,From,Event,To) +self.Accomplished=true +self:SetDetectionOff() +end +function AI_A2A_GCI:onafterDestroy(AIGroup,From,Event,To,EventData) +if EventData.IniUnit then +self.AttackUnits[EventData.IniUnit]=nil +end +end +function AI_A2A_GCI:OnEventDead(EventData) +self:F({"EventDead",EventData}) +if EventData.IniDCSUnit then +if self.AttackUnits and self.AttackUnits[EventData.IniUnit]then +self:__Destroy(1,EventData) +end +end +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.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:SetTacticalDisplay(false) +self:__Start(5) +return self +end +function AI_A2A_DISPATCHER:OnEventCrashOrDead(EventData) +self.Detection:ForgetDetectedUnit(EventData.IniUnitName) +end +function AI_A2A_DISPATCHER:OnEventLand(EventData) +self:E("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() +return +end +if DefenderUnit:GetLife()~=DefenderUnit:GetLife0()then +DefenderUnit:Destroy() +return +end +if DefenderUnit:GetFuel()<=self.DefenderDefault.FuelThreshold 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 then +local DefenderSize=Defender:GetSize() +if DefenderSize==1 then +self:RemoveDefenderFromSquadron(Squadron,Defender) +end +DefenderUnit:Destroy() +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: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: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()}) +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,Resources) +self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{} +local DefenderSquadron=self.DefenderSquadrons[SquadronName] +DefenderSquadron.Name=SquadronName +DefenderSquadron.Airbase=AIRBASE:FindByName(AirbaseName) +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.Resources=Resources +DefenderSquadron.TemplatePrefixes=TemplatePrefixes +self:E({Squadron={SquadronName,AirbaseName,TemplatePrefixes,Resources}}) +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:SetSquadronCap(SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType) +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.Zone=Zone +Cap.FloorAltitude=FloorAltitude +Cap.CeilingAltitude=CeilingAltitude +Cap.PatrolMinSpeed=PatrolMinSpeed +Cap.PatrolMaxSpeed=PatrolMaxSpeed +Cap.EngageMinSpeed=EngageMinSpeed +Cap.EngageMaxSpeed=EngageMaxSpeed +Cap.AltType=AltType +self:SetSquadronCapInterval(SquadronName,self.DefenderDefault.CapLimit,self.DefenderDefault.CapMinSeconds,self.DefenderDefault.CapMaxSeconds,1) +self:E({CAP={SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType}}) +local RecceSet=self.Detection:GetDetectionSetGroup() +RecceSet:FilterPrefixes(DefenderSquadron.TemplatePrefixes) +RecceSet:FilterStart() +self.Detection:SetFriendlyPrefixes(DefenderSquadron.TemplatePrefixes) +return self +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(not DefenderSquadron.Resources)or(DefenderSquadron.Resources and DefenderSquadron.Resources>0)then +local Cap=DefenderSquadron.Cap +if Cap then +local CapCount=self:CountCapAirborne(SquadronName) +self:E({CapCount=CapCount}) +if CapCount0)then +local Gci=DefenderSquadron.Gci +if Gci then +return DefenderSquadron +end +end +return nil +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:E({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: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:AddDefenderToSquadron(Squadron,Defender,Size) +self.Defenders=self.Defenders or{} +local DefenderName=Defender:GetName() +self.Defenders[DefenderName]=Squadron +if Squadron.Resources then +Squadron.Resources=Squadron.Resources-Size +end +self:E({DefenderName=DefenderName,SquadronResources=Squadron.Resources}) +end +function AI_A2A_DISPATCHER:RemoveDefenderFromSquadron(Squadron,Defender) +self.Defenders=self.Defenders or{} +local DefenderName=Defender:GetName() +if Squadron.Resources then +Squadron.Resources=Squadron.Resources+Defender:GetSize() +end +self.Defenders[DefenderName]=nil +self:F({DefenderName=DefenderName,SquadronResources=Squadron.Resources}) +end +function AI_A2A_DISPATCHER:GetSquadronFromDefender(Defender) +self.Defenders=self.Defenders or{} +local DefenderName=Defender:GetName() +self:F({DefenderName=DefenderName}) +return self.Defenders[DefenderName] +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:IsAlive()then +if DefenderTask.Fsm:Is("Patrolling")or DefenderTask.Fsm:Is("Engaging")or DefenderTask.Fsm:Is("Refuelling")then +CapCount=CapCount+1 +end +end +end +end +end +end +return CapCount +end +function AI_A2A_DISPATCHER:CountDefendersEngaged(AttackerDetection) +local DefenderCount=0 +self:E("Counting Defenders Engaged for Attacker:") +local DetectedSet=AttackerDetection.Set +DetectedSet:Flush() +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() +DefenderCount=DefenderCount+DefenderSize/SquadronOverhead +self:F("Defender Group Name: "..Defender:GetName()..", Size: "..DefenderSize) +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: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 Spawn=DefenderSquadron.Spawn[math.random(1,#DefenderSquadron.Spawn)] +local DefenderGrouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping +Spawn:InitGrouping(DefenderGrouping) +local TakeoffMethod=self:GetSquadronTakeoff(SquadronName) +local DefenderCAP=Spawn:SpawnAtAirbase(DefenderSquadron.Airbase,TakeoffMethod,DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude) +self:AddDefenderToSquadron(DefenderSquadron,DefenderCAP,DefenderGrouping) +if DefenderCAP then +local Fsm=AI_A2A_CAP:New(DefenderCAP,Cap.Zone,Cap.FloorAltitude,Cap.CeilingAltitude,Cap.PatrolMinSpeed,Cap.PatrolMaxSpeed,Cap.EngageMinSpeed,Cap.EngageMaxSpeed,Cap.AltType) +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:SetTanker(DefenderSquadron.TankerName or self.DefenderDefault.TankerName) +Fsm:Start() +self:SetDefenderTask(SquadronName,DefenderCAP,"CAP",Fsm) +function Fsm:onafterTakeoff(Defender,From,Event,To) +self:F({"GCI Birth",Defender:GetName()}) +local Dispatcher=Fsm:GetDispatcher() +local Squadron=Dispatcher:GetSquadronFromDefender(Defender) +if Squadron then +Fsm:__Patrol(2) +end +end +function Fsm:onafterRTB(Defender,From,Event,To) +self:F({"CAP RTB",Defender:GetName()}) +self:GetParent(self).onafterRTB(self,Defender,From,Event,To) +local Dispatcher=self:GetDispatcher() +Dispatcher:ClearDefenderTaskTarget(Defender) +end +function Fsm:onafterHome(Defender,From,Event,To,Action) +self:E({"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() +end +end +end +end +end +end +function AI_A2A_DISPATCHER:onafterENGAGE(From,Event,To,AttackerDetection,Defenders) +if Defenders then +for DefenderID,Defender in pairs(Defenders)do +local Fsm=self:GetDefenderTaskFsm(Defender) +Fsm:__Engage(1,AttackerDetection.Set) +self:SetDefenderTaskTarget(Defender,AttackerDetection) +end +end +end +function AI_A2A_DISPATCHER:onafterGCI(From,Event,To,AttackerDetection,DefendersMissing,DefenderFriendlies) +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:__Engage(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.Resources then +DefendersNeeded=DefenderSquadron.Resources +BreakLoop=true +end +while(DefendersNeeded>0)do +local Spawn=DefenderSquadron.Spawn[math.random(1,#DefenderSquadron.Spawn)] +local DefenderGrouping=(DefenderGrouping0 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(Target) +local DetectedSet=Target.Set +local FriendlyUnitsNearBy=self.Detection:GetFriendliesNearBy(Target) +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,Resources) +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:F({Airbases=AirbaseNames}) +self.Templates:Flush() +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 +for TemplateID,Template in pairs(self.Templates:GetSet())do +local Template=Template +self:F({Template=Template:GetName()}) +local TemplateCoord=Template:GetCoordinate() +if AirbaseZone:IsVec2InZone(TemplateCoord:GetVec2())then +Templates=Templates or{} +table.insert(Templates,Template:GetName()) +end +end +if Templates then +self:SetSquadron(AirbaseName,AirbaseName,Templates,Resources) +end +end +self.CAPTemplates=SET_GROUP:New() +self.CAPTemplates:FilterPrefixes(CapPrefixes) +self.CAPTemplates:FilterOnce() +for CAPID,CAPTemplate in pairs(self.CAPTemplates:GetSet())do +local CAPZone=ZONE_POLYGON:New(CAPTemplate:GetName(),CAPTemplate) +local AirbaseDistance=99999999 +local AirbaseClosest=nil +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()) +if Distance 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: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:OptionROTPassiveDefense() +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) +self:E("onafterTarget") +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:E({"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:E({"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, +} +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:SetFlightRandomization(2) +self:SetStartState("None") +self:AddTransition("*","Stop","Stopped") +self:AddTransition("None","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: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) +self:F({FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace}) +FollowGroupSet:Flush() +local FollowSet=FollowGroupSet:GetSet() +local i=0 +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 +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) +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) +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) +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) +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) +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) +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 +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 +end +return self +end +function AI_FORMATION:SetFlightRandomization(FlightRandomization) +self.FlightRandomization=FlightRandomization +return self +end +function AI_FORMATION:onenterFollowing(FollowGroupSet) +self:F() +self:T({self.FollowUnit.UnitName,self.FollowUnit:IsAlive()}) +if self.FollowUnit:IsAlive()then +local ClientUnit=self.FollowUnit +self:T({ClientUnit.UnitName}) +local CT1,CT2,CV1,CV2 +CT1=ClientUnit:GetState(self,"CT1") +if CT1==nil or CT1==0 then +ClientUnit:SetState(self,"CV1",ClientUnit:GetPointVec3()) +ClientUnit:SetState(self,"CT1",timer.getTime()) +else +CT1=ClientUnit:GetState(self,"CT1") +CT2=timer.getTime() +CV1=ClientUnit:GetState(self,"CV1") +CV2=ClientUnit:GetPointVec3() +ClientUnit:SetState(self,"CT1",CT2) +ClientUnit:SetState(self,"CV1",CV2) +end +FollowGroupSet:ForEachGroup( +function(FollowGroup,Formation,ClientUnit,CT1,CV1,CT2,CV2) +FollowGroup:OptionROTPassiveDefense() +FollowGroup:OptionROEReturnFire() +local GroupUnit=FollowGroup:GetUnit(1) +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",GroupUnit:GetPointVec3()) +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=GroupUnit:GetPointVec3() +GV2:AddX(math.random(-Formation.FlightRandomization/2,Formation.FlightRandomization/2)) +GV2:AddY(math.random(-Formation.FlightRandomization/2,Formation.FlightRandomization/2)) +GV2:AddZ(math.random(-Formation.FlightRandomization/2,Formation.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 CVI={x=CV2.x+CS*10*math.sin(Ca), +y=GH2.y-(Distance+FollowFormation.x)/5, +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=60 +local Speed=-(Distance+FollowFormation.x)/Time +local GS=Speed+CS +if Speed<0 then +Speed=0 +end +FollowGroup:RouteToVec3(GDV_Formation,GS) +end +end +end, +self,ClientUnit,CT1,CV1,CT2,CV2 +) +self:__Follow(-0.5) +end +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,From,Event,To) +self:E({ProcessUnit,From,Event,To}) +self:__Assign(1) +end +function ACT_ASSIGN_ACCEPT:onenterAssigned(ProcessUnit,From,Event,To) +env.info("in here") +self:E({ProcessUnit,From,Event,To}) +local ProcessGroup=ProcessUnit:GetGroup() +self.Task:Assign(ProcessUnit,ProcessUnit:GetPlayerName()) +end +end +do +ACT_ASSIGN_MENU_ACCEPT={ +ClassName="ACT_ASSIGN_MENU_ACCEPT", +} +function ACT_ASSIGN_MENU_ACCEPT:New(TaskName,TaskBriefing) +local self=BASE:Inherit(self,ACT_ASSIGN:New()) +self.TaskName=TaskName +self.TaskBriefing=TaskBriefing +return self +end +function ACT_ASSIGN_MENU_ACCEPT:Init(FsmAssign) +self.TaskName=FsmAssign.TaskName +self.TaskBriefing=FsmAssign.TaskBriefing +end +function ACT_ASSIGN_MENU_ACCEPT:Init(TaskName,TaskBriefing) +self.TaskBriefing=TaskBriefing +self.TaskName=TaskName +return self +end +function ACT_ASSIGN_MENU_ACCEPT:onafterStart(ProcessUnit,From,Event,To) +self:E({ProcessUnit,From,Event,To}) +self:GetCommandCenter():MessageTypeToGroup("Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled.",ProcessUnit:GetGroup(),MESSAGE.Type.Information) +local ProcessGroup=ProcessUnit:GetGroup() +self.Menu=MENU_GROUP:New(ProcessGroup,"Task "..self.TaskName.." acceptance") +self.MenuAcceptTask=MENU_GROUP_COMMAND:New(ProcessGroup,"Accept task "..self.TaskName,self.Menu,self.MenuAssign,self) +self.MenuRejectTask=MENU_GROUP_COMMAND:New(ProcessGroup,"Reject task "..self.TaskName,self.Menu,self.MenuReject,self) +end +function ACT_ASSIGN_MENU_ACCEPT:MenuAssign() +self:E() +self:__Assign(1) +end +function ACT_ASSIGN_MENU_ACCEPT:MenuReject() +self:E() +self:__Reject(1) +end +function ACT_ASSIGN_MENU_ACCEPT:onafterAssign(ProcessUnit,From,Event,To) +self:E({ProcessUnit.UnitNameFrom,Event,To}) +self.Menu:Remove() +end +function ACT_ASSIGN_MENU_ACCEPT:onafterReject(ProcessUnit,From,Event,To) +self:E({ProcessUnit.UnitName,From,Event,To}) +self.Menu:Remove() +ProcessUnit:Destroy() +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) +MENU_GROUP_COMMAND:New( +MenuGroup, +MenuText, +ParentMenu, +self.MenuCancel, +self +):SetTime(MenuTime) +return self +end +function ACT_ROUTE:SetRouteMode(RouteMode) +self.RouteMode=RouteMode +return self +end +function ACT_ROUTE:GetRouteText(Controllable) +self:E() +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:E({CC.ReferencePoints}) +for ZoneName,Zone in pairs(CC.ReferencePoints)do +self:E({ZoneName=ZoneName}) +local Zone=Zone +local ZoneCoord=Zone:GetCoordinate() +local ZoneDistance=ZoneCoord:Get2DDistance(self.Coordinate) +self:E({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 +self:T({DisplayCount=self.DisplayCount}) +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({self.Range}) +self.Range=Range or 10000 +end +function ACT_ROUTE_POINT:GetRange() +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="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=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="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:E({ProcessUnit=ProcessUnit}) +local RouteText=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) +self:E({ProcessUnit,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) +self:E(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", +} +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:HandleEvent(EVENTS.Birth, +function(self,EventData) +if EventData.IniObjectCategory==1 then +local EventGroup=GROUP:Find(EventData.IniDCSGroup) +if EventGroup and self:HasGroup(EventGroup)then +local MenuReporting=MENU_GROUP:New(EventGroup,"Missions Reports",self.CommandCenterMenu) +local MenuMissionsSummary=MENU_GROUP_COMMAND:New(EventGroup,"Missions Status Report",MenuReporting,self.ReportMissionsStatus,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() +_DATABASE:PlayerSettingsMenu(PlayerUnit) +end +end +end +) +self:HandleEvent(EVENTS.PlayerEnterUnit, +function(self,EventData) +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 +) +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) +return self +end +function COMMANDCENTER:GetName() +return self.CommandCenterName +end +function COMMANDCENTER:GetPositionable() +return self.CommandCenterPositionable +end +function COMMANDCENTER:GetMissions() +return self.Missions +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:F() +self.CommandCenterMenu=self.CommandCenterMenu or MENU_COALITION:New(self.CommandCenterCoalition,"Command Center ("..self:GetName()..")") +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 +local Mission=Mission +Mission:RemoveMenu(MenuTime) +end +end +function COMMANDCENTER:GetMenu() +return self.CommandCenterMenu +end +function COMMANDCENTER:HasGroup(MissionGroup) +local Has=false +for MissionID,Mission in pairs(self.Missions)do +local Mission=Mission +if Mission:HasGroup(MissionGroup)then +Has=true +break +end +end +return Has +end +function COMMANDCENTER:MessageToAll(Message) +self:GetPositionable():MessageToAll(Message,20,self:GetName()) +end +function COMMANDCENTER:MessageToGroup(Message,TaskGroup) +self:GetPositionable():MessageToGroup(Message,15,TaskGroup,self:GetName()) +end +function COMMANDCENTER:MessageTypeToGroup(Message,TaskGroup,MessageType) +self:GetPositionable():MessageTypeToGroup(Message,MessageType,TaskGroup,self:GetName()) +end +function COMMANDCENTER:MessageToCoalition(Message) +local CCCoalition=self:GetPositionable():GetCoalition() +self:GetPositionable():MessageToCoalition(Message,15,CCCoalition) +end +function COMMANDCENTER:MessageTypeToCoalition(Message,MessageType) +local CCCoalition=self:GetPositionable():GetCoalition() +self:GetPositionable():MessageTypeToCoalition(Message,MessageType,CCCoalition) +end +function COMMANDCENTER:ReportMissionsStatus(ReportGroup) +self:E(ReportGroup) +local Report=REPORT:New() +Report:Add("Status report of all missions.") +for MissionID,Mission in pairs(self.Missions)do +local Mission=Mission +Report:Add(" - "..Mission:ReportStatus()) +end +self:MessageToGroup(Report:Text(),ReportGroup) +end +function COMMANDCENTER:ReportMissionsPlayers(ReportGroup) +self:E(ReportGroup) +local Report=REPORT:New() +Report:Add("Players active in all missions.") +for MissionID,Mission in pairs(self.Missions)do +local Mission=Mission +Report:Add(" - "..Mission:ReportPlayers()) +end +self:MessageToGroup(Report:Text(),ReportGroup) +end +function COMMANDCENTER:ReportDetails(ReportGroup,Task) +self:E(ReportGroup) +local Report=REPORT:New() +for MissionID,Mission in pairs(self.Missions)do +local Mission=Mission +Report:Add(" - "..Mission:ReportDetails()) +end +self:MessageToGroup(Report:Text(),ReportGroup) +end +MISSION={ +ClassName="MISSION", +Name="", +MissionStatus="PENDING", +AssignedGroups={}, +} +function MISSION:New(CommandCenter,MissionName,MissionPriority,MissionBriefing,MissionCoalition) +local self=BASE:Inherit(self,FSM:New()) +self:T({MissionName,MissionPriority,MissionBriefing,MissionCoalition}) +self.CommandCenter=CommandCenter +CommandCenter:AddMission(self) +self.Name=MissionName +self.MissionPriority=MissionPriority +self.MissionBriefing=MissionBriefing +self.MissionCoalition=MissionCoalition +self.Tasks={} +self.PlayerNames={} +self:SetStartState("IDLE") +self:AddTransition("IDLE","Start","ENGAGED") +self:AddTransition("ENGAGED","Stop","IDLE") +self:AddTransition("ENGAGED","Complete","COMPLETED") +self:AddTransition("*","Fail","FAILED") +self:AddTransition("*","MissionGoals","*") +CommandCenter:SetMenu() +return self +end +function MISSION:onenterCOMPLETED(From,Event,To) +self:GetCommandCenter():MessageTypeToCoalition(self:GetName().." has been completed! Good job guys!",MESSAGE.Type.Information) +end +function MISSION:GetName() +return string.format('Mission "%s (%s)"',self.Name,self.MissionPriority) +end +function MISSION:JoinUnit(PlayerUnit,PlayerGroup) +self:F({PlayerUnit=PlayerUnit,PlayerGroup=PlayerGroup}) +local PlayerUnitAdded=false +for TaskID,Task in pairs(self:GetTasks())do +local Task=Task +if Task:JoinUnit(PlayerUnit,PlayerGroup)then +PlayerUnitAdded=true +end +end +return PlayerUnitAdded +end +function MISSION:AbortUnit(PlayerUnit) +self:F({PlayerUnit=PlayerUnit}) +for TaskID,Task in pairs(self:GetTasks())do +local Task=Task +local PlayerGroup=PlayerUnit:GetGroup() +Task:AbortGroup(PlayerGroup) +end +return self +end +function MISSION:CrashUnit(PlayerUnit) +self:F({PlayerUnit=PlayerUnit}) +for TaskID,Task in pairs(self:GetTasks())do +local Task=Task +local PlayerGroup=PlayerUnit:GetGroup() +Task:CrashGroup(PlayerGroup) +end +return self +end +function MISSION:AddScoring(Scoring) +self.Scoring=Scoring +return self +end +function MISSION:GetScoring() +return self.Scoring +end +function MISSION:GetGroups() +local SetGroup=SET_GROUP:New() +for TaskID,Task in pairs(self:GetTasks())do +local Task=Task +local GroupSet=Task:GetGroups() +GroupSet:ForEachGroup( +function(TaskGroup) +SetGroup:Add(TaskGroup,TaskGroup) +end +) +end +return SetGroup +end +function MISSION:SetMenu(MenuTime) +self:F({self:GetName(),MenuTime}) +for _,TaskData in pairs(self:GetTasks())do +local Task=TaskData +Task:SetMenu(MenuTime) +end +end +function MISSION:RemoveMenu(MenuTime) +self:F({self:GetName(),MenuTime}) +for _,Task in pairs(self:GetTasks())do +local Task=Task +Task:RemoveMenu(MenuTime) +end +end +do +function MISSION:IsGroupAssigned(MissionGroup) +local MissionGroupName=MissionGroup:GetName() +if self.AssignedGroups[MissionGroupName]==MissionGroup then +self:T({"Mission is assigned to:",MissionGroup:GetName()}) +return true +end +self:T({"Mission is not assigned to:",MissionGroup:GetName()}) +return false +end +function MISSION:SetGroupAssigned(MissionGroup) +local MissionName=self:GetName() +local MissionGroupName=MissionGroup:GetName() +self.AssignedGroups[MissionGroupName]=MissionGroup +self:E(string.format("Mission %s is assigned to %s",MissionName,MissionGroupName)) +return self +end +function MISSION:ClearGroupAssignment(MissionGroup) +local MissionName=self:GetName() +local MissionGroupName=MissionGroup:GetName() +self.AssignedGroups[MissionGroupName]=nil +return self +end +end +function MISSION:GetCommandCenter() +return self.CommandCenter +end +function MISSION:RemoveTaskMenu(Task) +Task:RemoveMenu() +end +function MISSION:GetRootMenu(TaskGroup) +local CommandCenter=self:GetCommandCenter() +local CommandCenterMenu=CommandCenter:GetMenu() +local MissionName=self:GetName() +self.MissionMenu=self.MissionMenu or MENU_COALITION:New(self.MissionCoalition,self:GetName(),CommandCenterMenu) +return self.MissionMenu +end +function MISSION:GetMenu(TaskGroup) +local CommandCenter=self:GetCommandCenter() +local CommandCenterMenu=CommandCenter:GetMenu() +local MissionName=self:GetName() +self.MissionGroupMenu=self.MissionGroupMenu or{} +self.MissionGroupMenu[TaskGroup]=self.MissionGroupMenu[TaskGroup]or{} +local GroupMenu=self.MissionGroupMenu[TaskGroup] +self.MissionMenu=self.MissionMenu or MENU_COALITION:New(self.MissionCoalition,self:GetName(),CommandCenterMenu) +GroupMenu.BriefingMenu=GroupMenu.BriefingMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Mission Briefing",self.MissionMenu,self.MenuReportBriefing,self,TaskGroup) +GroupMenu.TaskReportsMenu=GroupMenu.TaskReportsMenu or MENU_GROUP:New(TaskGroup,"Task Reports",self.MissionMenu) +GroupMenu.ReportTasksMenu=GroupMenu.ReportTasksMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Report Tasks",GroupMenu.TaskReportsMenu,self.MenuReportTasksSummary,self,TaskGroup) +GroupMenu.ReportPlannedTasksMenu=GroupMenu.ReportPlannedTasksMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Report Planned Tasks",GroupMenu.TaskReportsMenu,self.MenuReportTasksPerStatus,self,TaskGroup,"Planned") +GroupMenu.ReportAssignedTasksMenu=GroupMenu.ReportAssignedTasksMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Report Assigned Tasks",GroupMenu.TaskReportsMenu,self.MenuReportTasksPerStatus,self,TaskGroup,"Assigned") +GroupMenu.ReportSuccessTasksMenu=GroupMenu.ReportSuccessTasksMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Report Successful Tasks",GroupMenu.TaskReportsMenu,self.MenuReportTasksPerStatus,self,TaskGroup,"Success") +GroupMenu.ReportFailedTasksMenu=GroupMenu.ReportFailedTasksMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Report Failed Tasks",GroupMenu.TaskReportsMenu,self.MenuReportTasksPerStatus,self,TaskGroup,"Failed") +GroupMenu.ReportHeldTasksMenu=GroupMenu.ReportHeldTasksMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Report Held Tasks",GroupMenu.TaskReportsMenu,self.MenuReportTasksPerStatus,self,TaskGroup,"Hold") +GroupMenu.PlayerReportsMenu=GroupMenu.PlayerReportsMenu or MENU_GROUP:New(TaskGroup,"Statistics Reports",self.MissionMenu) +GroupMenu.ReportMissionHistory=GroupMenu.ReportPlayersHistory or MENU_GROUP_COMMAND:New(TaskGroup,"Report Mission Progress",GroupMenu.PlayerReportsMenu,self.MenuReportPlayersProgress,self,TaskGroup) +GroupMenu.ReportPlayersPerTaskMenu=GroupMenu.ReportPlayersPerTaskMenu or MENU_GROUP_COMMAND:New(TaskGroup,"Report Players per Task",GroupMenu.PlayerReportsMenu,self.MenuReportPlayersPerTask,self,TaskGroup) +return self.MissionMenu +end +function MISSION:GetTask(TaskName) +self:F({TaskName}) +return self.Tasks[TaskName] +end +function MISSION:AddTask(Task) +local TaskName=Task:GetTaskName() +self:F(TaskName) +self.Tasks[TaskName]=self.Tasks[TaskName]or{n=0} +self.Tasks[TaskName]=Task +self:GetCommandCenter():SetMenu() +return Task +end +function MISSION:RemoveTask(Task) +local TaskName=Task:GetTaskName() +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:GetNextTaskID(Task) +local TaskName=Task:GetTaskName() +self:F(TaskName) +self.Tasks[TaskName]=self.Tasks[TaskName]or{n=0} +self.Tasks[TaskName].n=self.Tasks[TaskName].n+1 +return self.Tasks[TaskName].n +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:GetName() +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:ReportStatus() +local Report=REPORT:New() +local Name=self:GetName() +local Status="<"..self:GetState()..">" +Report:Add(string.format('%s - Status "%s"',Name,Status)) +local TaskTypes=self:GetTaskTypes() +Report:Add(string.format(" - Task Types: %s",table.concat(TaskTypes,", "))) +local TaskStatusList={"Planned","Assigned","Success","Hold","Cancelled","Aborted","Failed"} +for TaskStatusID,TaskStatus in pairs(TaskStatusList)do +local TaskCount=0 +local TaskPlayerCount=0 +for TaskID,Task in pairs(self:GetTasks())do +local Task=Task +if Task:Is(TaskStatus)then +TaskCount=TaskCount+1 +TaskPlayerCount=TaskPlayerCount+Task:GetPlayerCount() +end +end +if TaskCount>0 then +Report:Add(string.format(" - %02d %s Tasks (%dp)",TaskCount,TaskStatus,TaskPlayerCount)) +end +end +return Report:Text() +end +function MISSION:ReportPlayersPerTask(ReportGroup) +local Report=REPORT:New() +local Name=self:GetName() +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:GetName() +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 TaskGoalTotal=Task:GetGoalTotal()or 0 +local TaskName=Task:GetName() +PlayerList[TaskName]=PlayerList[TaskName]or{} +if TaskGoalTotal~=0 then +local PlayerNames=self:GetPlayerNames() +for PlayerName,PlayerData in pairs(PlayerNames)do +PlayerList[TaskName][PlayerName]=string.format('Player (%s): Task "%s": %d%%',PlayerName,TaskName,Task:GetPlayerProgress(PlayerName)*100/TaskGoalTotal) +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:ReportSummary(ReportGroup) +local Report=REPORT:New() +local Name=self:GetName() +local Status="<"..self:GetState()..">" +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:GetName() +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(Task:ReportDetails(ReportGroup)) +end +return Report:Text() +end +function MISSION:GetTasks() +return self.Tasks +end +function MISSION:MenuReportBriefing(ReportGroup) +local Report=self:ReportBriefing() +self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Briefing) +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()) +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("Assigned","Abort","Aborted") +self:AddTransition("Assigned","Cancel","Cancelled") +self:AddTransition("Assigned","Goal","*") +self:AddTransition("*","PlayerCrashed","*") +self:AddTransition("*","PlayerAborted","*") +self:AddTransition("*","PlayerDead","*") +self:AddTransition({"Failed","Aborted","Cancelled"},"Replan","Planned") +self:AddTransition("*","TimeOut","Cancelled") +self:E("New TASK "..TaskName) +self.Processes={} +self.Fsm={} +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.FsmTemplate=self.FsmTemplate or FSM_PROCESS:New() +self.TaskInfo={} +self.TaskProgress={} +return self +end +function TASK:GetUnitProcess(TaskUnit) +if TaskUnit then +return self:GetStateMachine(TaskUnit) +else +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:E({IsGroupAssigned=IsGroupAssigned}) +if IsGroupAssigned then +self:AssignToUnit(PlayerUnit) +self:MessageToGroups(PlayerUnit:GetPlayerName().." joined Task "..self:GetName()) +end +end +end +return PlayerUnitAdded +end +function TASK:AbortGroup(PlayerGroup) +self:F({PlayerGroup=PlayerGroup}) +local PlayerGroups=self:GetGroups() +if PlayerGroups:IsIncludeObject(PlayerGroup)then +if self:IsStateAssigned()then +local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup) +self:E({IsGroupAssigned=IsGroupAssigned}) +if IsGroupAssigned then +local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName() +self:UnAssignFromGroup(PlayerGroup) +PlayerGroups:Flush() +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:E({IsGroupAssigned=IsGroupAssigned}) +if IsGroupAssigned then +local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName() +self:MessageToGroups(PlayerName.." crashed! ") +self:UnAssignFromGroup(PlayerGroup) +PlayerGroups:Flush() +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 +do +function TASK:IsGroupAssigned(TaskGroup) +local TaskGroupName=TaskGroup:GetName() +if self.AssignedGroups[TaskGroupName]then +self:T({"Task is assigned to:",TaskGroup:GetName()}) +return true +end +self:T({"Task is not assigned to:",TaskGroup:GetName()}) +return false +end +function TASK:SetGroupAssigned(TaskGroup) +local TaskName=self:GetName() +local TaskGroupName=TaskGroup:GetName() +self.AssignedGroups[TaskGroupName]=TaskGroup +self:E(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: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:E(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() +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() +end +end +function TASK:HasGroup(FindGroup) +local SetAttackGroup=self:GetGroups() +return SetAttackGroup:FindGroup(FindGroup) +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) +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 +local TaskGroup=TaskGroup +CC:MessageToGroup(Message,TaskGroup,TaskGroup:GetName()) +end +end +function TASK:SendBriefingToAssignedGroups() +self:F2() +for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do +if self:IsGroupAssigned(TaskGroup)then +TaskGroup:Message(self.TaskBriefing,60) +end +end +end +function TASK:UnAssignFromGroups() +self:F2() +for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do +if self:IsGroupAssigned(TaskGroup)then +self:UnAssignFromGroup(TaskGroup) +end +end +end +function TASK:HasAliveUnits() +self:F() +for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do +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 +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()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 CommandCenter=Mission:GetCommandCenter() +local CommandCenterMenu=CommandCenter:GetMenu() +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()) +local MissionMenu=Mission:GetMenu(TaskGroup) +self.MenuPlanned=self.MenuPlanned or{} +self.MenuPlanned[TaskGroup]=MENU_GROUP:New(TaskGroup,"Join Planned Task",MissionMenu,Mission.MenuReportTasksPerStatus,Mission,TaskGroup,"Planned"):SetTime(MenuTime):SetTag("Tasking") +local TaskTypeMenu=MENU_GROUP:New(TaskGroup,TaskType,self.MenuPlanned[TaskGroup]):SetTime(MenuTime):SetTag("Tasking"):SetRemoveParent(true) +local TaskTypeMenu=MENU_GROUP:New(TaskGroup,TaskText,TaskTypeMenu):SetTime(MenuTime):SetTag("Tasking"):SetRemoveParent(true) +local ReportTaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Report Task Status"),TaskTypeMenu,self.MenuTaskStatus,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking"):SetRemoveParent(true) +if not Mission:IsGroupAssigned(TaskGroup)then +self:F({"Replacing Join Task menu"}) +local JoinTaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Join Task"),TaskTypeMenu,self.MenuAssignToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking"):SetRemoveParent(true) +local MarkTaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Mark Task on Map"),TaskTypeMenu,self.MenuMarkToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking"):SetRemoveParent(true) +end +return self +end +function TASK:SetAssignedMenuForGroup(TaskGroup,MenuTime) +self:F({TaskGroup:GetName(),MenuTime}) +local Mission=self:GetMission() +local MissionName=Mission:GetName() +local CommandCenter=Mission:GetCommandCenter() +local CommandCenterMenu=CommandCenter:GetMenu() +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()) +local MissionMenu=Mission:GetMenu(TaskGroup) +self.MenuAssigned=self.MenuAssigned or{} +self.MenuAssigned[TaskGroup]=MENU_GROUP:New(TaskGroup,string.format("Assigned Task %s",TaskName),MissionMenu):SetTime(MenuTime):SetTag("Tasking") +local TaskTypeMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Report Task Status"),self.MenuAssigned[TaskGroup],self.MenuTaskStatus,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking"):SetRemoveParent(true) +local TaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Abort Group from Task"),self.MenuAssigned[TaskGroup],self.MenuTaskAbort,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking"):SetRemoveParent(true) +return self +end +function TASK:RemoveMenu(MenuTime) +self:F({self:GetName(),MenuTime}) +for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do +local TaskGroup=TaskGroup +self:RefreshMenus(TaskGroup,MenuTime) +end +end +function TASK:RefreshMenus(TaskGroup,MenuTime) +self:F({TaskGroup:GetName(),MenuTime}) +local Mission=self:GetMission() +local MissionName=Mission:GetName() +local CommandCenter=Mission:GetCommandCenter() +local CommandCenterMenu=CommandCenter:GetMenu() +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 +PlannedMenu:Remove(MenuTime,"Tasking") +end +if AssignedMenu then +AssignedMenu:Remove(MenuTime,"Tasking") +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:E("Join Task menu selected") +self:AssignToGroup(TaskGroup) +end +function TASK:MenuMarkToGroup(TaskGroup) +self:E("Mark Task menu selected") +self:UpdateTaskInfo() +local Report=REPORT:New():SetIndent(0) +local Name=self:GetName() +Report:Add(Name..": "..self:GetTaskBriefing()) +for TaskInfoID,TaskInfo in pairs(self.TaskInfo,function(t,a,b)return t[a].TaskInfoOrder") +if self.TaskInfo["Coordinates"]then +local TaskInfoIDText=string.format("%s: ","Coordinate") +local TaskCoord=self.TaskInfo["Coordinates"].TaskInfoText +Report:Add(TaskInfoIDText..TaskCoord:ToString(ReportGroup,nil,self)) +end +return Report:Text(', ') +end +function TASK:ReportOverview(ReportGroup) +self:UpdateTaskInfo() +local TaskName=self:GetName() +local Report=REPORT:New() +local Line=0 +local LineReport=REPORT:New() +for TaskInfoID,TaskInfo in UTILS.spairs(self.TaskInfo,function(t,a,b)return t[a].TaskInfoOrder" +Report:Add("Task: "..Name.." - "..Status.." - Detailed Report") +local PlayerNames=self:GetPlayerNames() +local PlayerReport=REPORT:New() +for PlayerName,PlayerGroup in pairs(PlayerNames)do +PlayerReport:Add("Group "..PlayerGroup:GetCallsign()..": "..PlayerName) +end +local Players=PlayerReport:Text() +if Players~=""then +Report:Add(" - Players assigned:") +Report:AddIndent(Players) +end +for TaskInfoID,TaskInfo in pairs(self.TaskInfo,function(t,a,b)return t[a].TaskInfoOrder0 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) +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) +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:E() +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:GetDetectedItem(TaskIndex) +if not DetectedItem then +local TaskText=Task:GetName() +for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do +Mission:GetCommandCenter():MessageToGroup(string.format("Obsolete A2G task %s for %s removed.",TaskText,Mission:GetName()),TaskGroup) +end +Task=self:RemoveTask(TaskIndex) +Mission:RemoveTask(Task) +self.Tasks[TaskIndex]=nil +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:E({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:UpdateTaskInfo() +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,TaskIndex) +Task:UpdateTaskInfo() +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,TaskIndex) +Task:UpdateTaskInfo() +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~=""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:UpdateTaskInfo() +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,TaskIndex) +Task:UpdateTaskInfo() +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,TaskIndex) +Task:UpdateTaskInfo() +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) +Task:SetDetection(Detection,TaskIndex) +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) +Task:SetDetection(Detection,TaskIndex) +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) +Task:SetDetection(Detection,TaskIndex) +end +end +end +if Task then +self.Tasks[TaskIndex]=Task +Task:SetTargetZone(DetectedZone) +Task:SetDispatcher(self) +Task:UpdateTaskInfo() +Mission:AddTask(Task) +TaskReport:Add(Task:GetName()) +else +self:E("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~=""then +Mission:GetCommandCenter():MessageToGroup(string.format("%s has tasks %s. Subscribe to a task using the radio menu.",Mission:GetName(),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:AddProcess("Planned","Accept",ACT_ASSIGN_ACCEPT:New(self.TaskBriefing),{Assigned="RouteToRendezVous",Rejected="Reject"}) +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:onafterRouteToRendezVous(TaskUnit,Task) +self:E({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:E({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) +self:__Engage(0.1) +end +function Fsm:onafterEngage(TaskUnit,Task) +self:E({self}) +self:__Account(0.1) +self:__RouteToTarget(0.1) +self:__RouteToTargets(-10) +end +function Fsm:onafterRouteToTarget(TaskUnit,Task) +self:E({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:E({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 +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:UpdateTaskInfo() +local TargetCoordinate=self.Detection and self.Detection:GetDetectedItemCoordinate(self.DetectedItemIndex)or self.TargetSetUnit:GetFirst():GetCoordinate() +self:SetInfo("Coordinates",TargetCoordinate,0) +local ThreatLevel,ThreatText +if self.Detection then +ThreatLevel,ThreatText=self.Detection:GetDetectedItemThreatLevel(self.DetectedItemIndex) +else +ThreatLevel,ThreatText=self.TargetSetUnit:CalculateThreatLevelA2G() +end +self:SetInfo("Threat",ThreatText.." ["..string.rep("■",ThreatLevel).."]",11) +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:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,ReportTypes:Text(", ")),10) +else +local DetectedItemsCount=self.TargetSetUnit:Count() +local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() +self:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,DetectedItemsTypes),10) +end +end +function TASK_A2G_SEAD:ReportOrder(ReportGroup) +local Coordinate=self:GetInfo("Coordinates") +local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) +return Distance +end +function TASK_A2G_SEAD:onafterGoal(TaskUnit,From,Event,To) +local TargetSetUnit=self.TargetSetUnit +if TargetSetUnit:Count()==0 then +self:Success() +end +self:__Goal(-10) +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:UpdateTaskInfo() +self:E({self.Detection,self.DetectedItemIndex}) +local TargetCoordinate=self.Detection and self.Detection:GetDetectedItemCoordinate(self.DetectedItemIndex)or self.TargetSetUnit:GetFirst():GetCoordinate() +self:SetInfo("Coordinates",TargetCoordinate,0) +local ThreatLevel,ThreatText +if self.Detection then +ThreatLevel,ThreatText=self.Detection:GetDetectedItemThreatLevel(self.DetectedItemIndex) +else +ThreatLevel,ThreatText=self.TargetSetUnit:CalculateThreatLevelA2G() +end +self:SetInfo("Threat",ThreatText.." ["..string.rep("■",ThreatLevel).."]",11) +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:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,ReportTypes:Text(", ")),10) +else +local DetectedItemsCount=self.TargetSetUnit:Count() +local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() +self:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,DetectedItemsTypes),10) +end +end +function TASK_A2G_BAI:ReportOrder(ReportGroup) +local Coordinate=self:GetInfo("Coordinates") +local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) +return Distance +end +function TASK_A2G_BAI:onafterGoal(TaskUnit,From,Event,To) +local TargetSetUnit=self.TargetSetUnit +if TargetSetUnit:Count()==0 then +self:Success() +end +self:__Goal(-10) +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:UpdateTaskInfo() +local TargetCoordinate=(self.Detection and self.Detection:GetDetectedItemCoordinate(self.DetectedItemIndex))or self.TargetSetUnit:GetFirst():GetCoordinate() +self:SetInfo("Coordinates",TargetCoordinate,0) +local ThreatLevel,ThreatText +if self.Detection then +ThreatLevel,ThreatText=self.Detection:GetDetectedItemThreatLevel(self.DetectedItemIndex) +else +ThreatLevel,ThreatText=self.TargetSetUnit:CalculateThreatLevelA2G() +end +self:SetInfo("Threat",ThreatText.." ["..string.rep("■",ThreatLevel).."]",11) +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:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,ReportTypes:Text(", ")),10) +else +local DetectedItemsCount=self.TargetSetUnit:Count() +local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() +self:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,DetectedItemsTypes),10) +end +end +function TASK_A2G_CAS:ReportOrder(ReportGroup) +local Coordinate=self:GetInfo("Coordinates") +local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) +return Distance +end +function TASK_A2G_CAS:onafterGoal(TaskUnit,From,Event,To) +local TargetSetUnit=self.TargetSetUnit +if TargetSetUnit:Count()==0 then +self:Success() +end +self:__Goal(-10) +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.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: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) +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:E() +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:GetDetectedItem(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:GetName()),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,TaskIndex) +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,TaskIndex) +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,TaskIndex) +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) +TaskReport:Add(Task:GetName()) +else +self:E("This should not happen") +end +end +if Task then +local FriendliesCount,FriendliesReport=self:GetFriendliesNearBy(DetectedItem) +Task:SetInfo("Friendlies",string.format("%d ( %s )",FriendliesCount,FriendliesReport:Text(",")),30) +local PlayersCount,PlayersReport=self:GetPlayerFriendliesNearBy(DetectedItem) +Task:SetInfo("Players",string.format("%d ( %s )",PlayersCount,PlayersReport:Text(",")),31) +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~=""then +Mission:GetCommandCenter():MessageToGroup(string.format("%s has tasks %s. Subscribe to a task using the radio menu.",Mission:GetName(),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:AddProcess("Planned","Accept",ACT_ASSIGN_ACCEPT:New(self.TaskBriefing),{Assigned="RouteToRendezVous",Rejected="Reject"}) +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:onafterRouteToRendezVous(TaskUnit,Task) +self:E({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:E({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()}) +self:__Engage(0.1) +end +function Fsm:onafterEngage(TaskUnit,Task) +self:E({self}) +self:__Account(0.1) +self:__RouteToTarget(0.1) +self:__RouteToTargets(-10) +end +function Fsm:onafterRouteToTarget(TaskUnit,Task) +self:E({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:GetCoordinate() +self:T({TargetCoordinate=Coordinate,Coordinate:GetX(),Coordinate:GetAlt(),Coordinate:GetZ()}) +Task:SetTargetCoordinate(TargetUnit:GetCoordinate(),TaskUnit) +end +self:__RouteToTargetPoint(0.1) +end +end +function Fsm:onafterRouteToTargets(TaskUnit,Task) +self:E({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: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 +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" +) +self:UpdateTaskInfo() +return self +end +function TASK_A2A_INTERCEPT:UpdateTaskInfo() +local TargetCoordinate=self.Detection and self.Detection:GetDetectedItemCoordinate(self.DetectedItemIndex)or self.TargetSetUnit:GetFirst():GetCoordinate() +self:SetInfo("Coordinates",TargetCoordinate,0) +self:SetInfo("Threat","["..string.rep("■",self.Detection and self.Detection:GetDetectedItemThreatLevel(self.DetectedItemIndex)or self.TargetSetUnit:CalculateThreatLevelA2G()).."]",11) +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:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,ReportTypes:Text(", ")),10) +else +local DetectedItemsCount=self.TargetSetUnit:Count() +local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() +self:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,DetectedItemsTypes),10) +end +end +function TASK_A2A_INTERCEPT:ReportOrder(ReportGroup) +self:F({TaskInfo=self.TaskInfo}) +local Coordinate=self.TaskInfo.Coordinates.TaskInfoText +local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) +return Distance +end +function TASK_A2A_INTERCEPT:onafterGoal(TaskUnit,From,Event,To) +local TargetSetUnit=self.TargetSetUnit +if TargetSetUnit:Count()==0 then +self:Success() +end +self:__Goal(-10) +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" +) +self:UpdateTaskInfo() +return self +end +function TASK_A2A_SWEEP:UpdateTaskInfo() +local TargetCoordinate=self.Detection and self.Detection:GetDetectedItemCoordinate(self.DetectedItemIndex)or self.TargetSetUnit:GetFirst():GetCoordinate() +self:SetInfo("Coordinates",TargetCoordinate,0) +self:SetInfo("Assumed Threat","["..string.rep("■",self.Detection and self.Detection:GetDetectedItemThreatLevel(self.DetectedItemIndex)or self.TargetSetUnit:CalculateThreatLevelA2G()).."]",11) +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:SetInfo("Lost Targets",string.format("%d of %s",DetectedItemsCount,ReportTypes:Text(", ")),10) +else +local DetectedItemsCount=self.TargetSetUnit:Count() +local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() +self:SetInfo("Lost Targets",string.format("%d of %s",DetectedItemsCount,DetectedItemsTypes),10) +end +end +function TASK_A2A_SWEEP:ReportOrder(ReportGroup) +local Coordinate=self.TaskInfo.Coordinates.TaskInfoText +local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) +return Distance +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" +) +self:UpdateTaskInfo() +return self +end +function TASK_A2A_ENGAGE:UpdateTaskInfo() +local TargetCoordinate=self.Detection and self.Detection:GetDetectedItemCoordinate(self.DetectedItemIndex)or self.TargetSetUnit:GetFirst():GetCoordinate() +self:SetInfo("Coordinates",TargetCoordinate,0) +self:SetInfo("Threat","["..string.rep("■",self.Detection and self.Detection:GetDetectedItemThreatLevel(self.DetectedItemIndex)or self.TargetSetUnit:CalculateThreatLevelA2G()).."]",11) +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:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,ReportTypes:Text(", ")),10) +else +local DetectedItemsCount=self.TargetSetUnit:Count() +local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames() +self:SetInfo("Targets",string.format("%d of %s",DetectedItemsCount,DetectedItemsTypes),10) +end +end +function TASK_A2A_ENGAGE:ReportOrder(ReportGroup) +local Coordinate=self.TaskInfo.Coordinates.TaskInfoText +local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate) +return Distance +end +function TASK_A2A_ENGAGE:onafterGoal(TaskUnit,From,Event,To) +local TargetSetUnit=self.TargetSetUnit +if TargetSetUnit:Count()==0 then +self:Success() +end +self:__Goal(-10) +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=2 +self.DeployZones={} +local Fsm=self:GetUnitProcess() +Fsm:SetStartState("Planned") +Fsm:AddProcess("Planned","Accept",ACT_ASSIGN_ACCEPT:New(self.TaskBriefing),{Assigned="SelectAction",Rejected="Reject"}) +Fsm:AddTransition({"Assigned","WaitingForCommand","ArrivedAtPickup","ArrivedAtDeploy","Boarded","UnBoarded","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","WaitingForCommand") +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","WaitingForCommand") +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("*","PrepareUnBoarding","AwaitUnBoarding") +Fsm:AddTransition("AwaitUnBoarding","UnBoard","UnBoarding") +Fsm:AddTransition("UnBoarding","UnBoarded","UnBoarded") +Fsm:AddTransition("Deployed","Success","Success") +Fsm:AddTransition("Rejected","Reject","Aborted") +Fsm:AddTransition("Failed","Fail","Failed") +function Fsm:onafterSelectAction(TaskUnit,Task) +local TaskUnitName=TaskUnit:GetName() +self:E({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()}) +local MenuTime=timer.getTime() +TaskUnit.Menu=MENU_GROUP:New(TaskUnit:GetGroup(),Task:GetName().." @ "..TaskUnit:GetName()):SetTime(MenuTime) +local CargoItemCount=TaskUnit:CargoItemCount() +Task.SetCargo:ForEachCargo( +function(Cargo) +if Cargo:IsAlive()then +if Cargo:IsUnLoaded()then +if CargoItemCount0 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 ActRouteCargo=ProcessUnit:GetProcess("RoutingToPickup","RouteToPickupPoint") +ActRouteCargo:Reset() +ActRouteCargo:SetCoordinate(Cargo:GetCoordinate()) +ActRouteCargo:SetRange(Cargo:GetBoardingRange()) +ActRouteCargo:SetMenuCancel(TaskUnit:GetGroup(),"Cancel Routing to Cargo "..Cargo:GetName(),TaskUnit.Menu) +ActRouteCargo:Start() +return self +end +function TASK_CARGO:SetDeployZone(DeployZone,TaskUnit) +local ProcessUnit=self:GetUnitProcess(TaskUnit) +local ActRouteDeployZone=ProcessUnit:GetProcess("RoutingToDeploy","RouteToDeployZone") +ActRouteDeployZone:Reset() +ActRouteDeployZone:SetZone(DeployZone) +ActRouteDeployZone:SetMenuCancel(TaskUnit:GetGroup(),"Cancel Routing to Deploy Zone"..DeployZone:GetName(),TaskUnit.Menu) +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)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 +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) +self:AddTransition("*","CargoPickedUp","*") +self:AddTransition("*","CargoDeployed","*") +self:E({CargoDeployed=self.CargoDeployed~=nil and"true"or"false"}) +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 true +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 +if Cargo:IsDeployed()then +for DeployZoneID,DeployZone in pairs(DeployZones)do +self:T({Cargo.CargoObject}) +if Cargo:IsInZone(DeployZone)==false then +CargoDeployed=false +end +end +else +CargoDeployed=false +end +end +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 +_EVENTDISPATCHER=EVENT:New() +_SCHEDULEDISPATCHER=SCHEDULEDISPATCHER:New() +_DATABASE=DATABASE:New() +_SETTINGS=SETTINGS:Set() +BASE:TraceOnOff(false) env.info('*** MOOSE INCLUDE END *** ')