-- Tacview ACMI - Universal Flight Analysis Tool 1.1.1 -- Export script for DCS: Black Shark, DCS: A-10C Warthog, Lock-On: Flaming Cliffs 1.2 -- Copyright (C) 2006-2011 - Stra Software -- http://tacview.strasoftware.com/ -- TO ENABLE THIS SCRIPT: -- Set [EnableExportScript = true] in [./Config/export/config.lua] -- Add [dofile("./Config/Export/TacviewExportDCS.lua")] at the end of [./Config/Export/Export.lua] -- ACMI text files are exported to %TACVIEW_EXPORT_PATH% folder -- if this environment variable is not valid, files are exported to [./Temp/] folder -- Headers dofile("./Config/World/World.lua") -- Required to get mission date dofile("./Scripts/Database/wsTypes.lua") -- Required to get object types definitions -- Tacview Exporter Tacview= { -- Log parameters DefaultObjectsUpdatePeriod=1/10, -- Delay between two log updates (in seconds) for default objects, use [0] to force update at each frame BallisticObjectsUpdatePeriod=1/2, -- Delay between two log updates (in seconds) for ballistic objects, use [0] to force update at each frame LatitudeOffset=41, -- To improve log resolution LongitudeOffset=36, -- To improve log resolution -- Convert Lock-On Object_Type To Tacview Object_Type -- \LockOn Flaming Cliffs 2\Scripts\Database\wsTypes.lua -- \LockOn Flaming Cliffs 2\Scripts\Database\wsTypesTree.lua ObjectTypeLookupTable= { [-1] = -1, -- Invalid object type [0.0.0.0] first repported during network flights on DCS 1.1.1 [wsType_Air]= { [-1] = 0x10, -- Aeroplanes (including fixed-wing drones) [wsType_Airplane]= { [-1] = 0x10, -- Aeroplanes (including fixed-wing drones) [wsType_Fighter] = 0x10, -- Aeroplanes (including fixed-wing drones) [wsType_F_Bomber] = 0x10, -- Aeroplanes (including fixed-wing drones) [wsType_Intercepter] = 0x10, -- Aeroplanes (including fixed-wing drones) [wsType_Intruder] = 0x10, -- Aeroplanes (including fixed-wing drones) [wsType_Cruiser] = 0x10, -- Aeroplanes (including fixed-wing drones) [wsType_Battleplane] = 0x10, -- Aeroplanes (including fixed-wing drones) }, [wsType_Helicopter] = 0x18, -- Helicopters (including rotary-wing drones) [wsType_Free_Fall]= { [-1] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Snars]= { [-1] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Chaff] = 0x50, -- Chaff (obviously a chaff cluster) [wsType_Flare] = 0x54, -- Flare }, [wsType_Parts]= { [-1] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [PILOT_PARASHUT] = 0x2e, -- Parachutist [PILOT_PARASHUT_US] = 0x2e, -- Parachutist [203] = 0x60, -- Minor object (e.g. cartridge) }, [wsType_FuelTank] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) }, }, [wsType_Ground]= { [-1] = 0x28, -- Light/Unarmed vehicles [wsType_Moving]= { [-1] = 0x28, -- Light/Unarmed vehicles [wsType_NoWeapon] = 0x28, -- Light/Unarmed vehicles [wsType_Gun] = 0x28, -- Light/Unarmed vehicles [wsType_Miss] = 0x24, -- Armored fighting vehicle [wsType_ChildMiss] = 0x24, -- Armored fighting vehicle [wsType_MissGun] = 0x24, -- Armored fighting vehicle [wsType_Civil] = 0x28, -- Light/Unarmed vehicles }, [wsType_Tank]= { [-1] = 0x24, -- Armored fighting vehicle [wsType_Gun]= { [-1] = 0x24, -- Armored fighting vehicle [91] = 0x2c, -- (wsTypeAutogun) Infantry [93] = 0x2c, -- (wsTypeSoldier_AK) Infantry [96] = 0x89, -- (wsTypeSandbox) Building [97] = 0x89, -- (wsTypeBunker) Building }, [wsType_MissGun]= { [-1] = 0x24, -- Armored fighting vehicle [90] = 0x2c, -- (wsTypeRPG) Infantry [94] = 0x2c, -- (wsTypeSoldier_RPG) Infantry }, [wsType_NoWeapon]= { [-1] = 0x28, -- Light/Unarmed vehicles [72] = 0x28, -- (wsTypePredator_GCS) Light/Unarmed vehicles [73] = 0x28, -- (wsTypePredator_TrojanSpirit) Light/Unarmed vehicles }, }, [wsType_SAM]= { [-1] = 0x20, -- Anti-aircraft warfare (usually: SAM and AAA) [wsType_Miss]= { [-1] = 0x20, -- Anti-aircraft warfare (usually: SAM and AAA) [IglaGRG_2] = 0x2c, -- Infantry [IglaRUS_2] = 0x2c, -- Infantry [StingerIZR_2] = 0x2c, -- Infantry [StingerUSA_2] = 0x2c, -- Infantry } }, [wsType_Standing]= { [-1] = 0x89, -- Building [wsType_NoWeapon] = 0x89, -- Building [wsType_Gun] = 0x89, -- Building [wsType_Miss] = 0x20, -- Anti-aircraft warfare (usually: SAM and AAA) [wsType_ChildMiss] = 0x20, -- Anti-aircraft warfare (usually: SAM and AAA) [wsType_MissGun] = 0x20, -- Anti-aircraft warfare (usually: SAM and AAA) [wsType_Civil] = 0x89, -- Building }, }, [wsType_Navy]= { [-1] = 0x30, -- Armed Watercraft (including submarines) [wsType_Ship]= { [-1] = 0x30, -- Armed Watercraft (including submarines) [wsType_AirCarrier] = 0x34, -- Aircraft carrier (including Helicopter carrier) [wsType_HCarrier] = 0x34, -- Aircraft carrier (including Helicopter carrier) [wsType_ArmedShip] = 0x30, -- Armed Watercraft (including submarines) [wsType_CivilShip] = 0x38, -- Light/Unarmed Watercraft }, }, [wsType_Weapon]= { [-1] = 0x40, -- Missile (guided missiles) [wsType_GContainer]= { [-1] = 0x46, -- External equipment / Shrapnel (e.g. Drop tank, cluster-bomb fragments, exploded plane parts, ...) }, [wsType_Missile]= { [-1] = 0x40, -- Missile (guided missiles) [wsType_AA_Missile] = 0x40, -- Missile (guided missiles) [wsType_AS_Missile] = 0x40, -- Missile (guided missiles) [wsType_SA_Missile] = 0x40, -- Missile (guided missiles) [wsType_SS_Missile] = 0x40, -- Missile (guided missiles) [wsType_AA_TRAIN_Missile] = 0x40, -- Missile (guided missiles) [wsType_AS_TRAIN_Missile] = 0x40, -- Missile (guided missiles) }, [wsType_Bomb]= { [-1] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_A] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_Guided] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_BetAB] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_Cluster] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_Antisubmarine] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_ODAB] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_Fire] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_Nuclear] = 0x4c, -- Bomb (guided and unguided) [wsType_Bomb_Lighter] = 0x4c, -- Bomb (guided and unguided) }, [wsType_Shell]= { [-1] = 0x48, -- Shell [wsType_Shell_A] = 0x48, -- Shell }, [wsType_NURS]= { [-1] = 0x44, -- Rocket (unguided missiles) [wsType_Container] = 0x44, -- Rocket (unguided missiles) [wsType_Rocket] = 0x44, -- Rocket (unguided missiles) }, }, [wsType_Static]= { [-1] = 0x89, -- Building [wsType_AirdromePart] = 0x89, -- Building [wsType_WingPart] = 0x89, -- Building [wsType_Free_Fall]= { [-1] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Parts]= { [-1] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [255] = -1, -- Particles (e.g. landing gear dust on runway) }, }, [wsType_Airdrome]= { [-1] = 0x81, -- Aerodrome [wsType_RW1] = 0x81, -- Aerodrome [wsType_RW2] = 0x81, -- Aerodrome [wsType_Heliport] = 0x81, -- Aerodrome }, [wsType_Explosion]= { [-1] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_GroundExp] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) }, [wsType_GContainer]= { [-1] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Control_Cont] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Jam_Cont] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Cannon_Cont] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Support] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Snare_Cont] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) [wsType_Smoke_Cont] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) }, }, [wsType_Destroyed] = 0x46, -- Shrapnel (e.g. Cluster-bomb fragments or exploded plane parts) }, -- Object Names Table (used to fix missing names DCS bug) -- See: \dcs a-10c warthog\Scripts\Database\wsTypes.lua -- Some types are used for multiple units like [2,17,26,93], in that case I'm using a generic name [Infantry] ObjectNamesLookupTable= { [wsType_Ground]= -- 2 { [wsType_SAM]= -- 16 { [wsType_Gun]= -- 26 { [ZU_23_insurgent] = "ZU-23-2", -- 70 [ZU_23_insurgent_okop] = "ZU-23-2", -- 71 [ZU_23_insurgent_ural] = "Ural-375 ZU-23", -- 72 }, [wsType_Miss]= -- 27 { [IglaRUS_1] = "SAM SA-18 Igla-S manpad", -- 52 [IglaRUS_2] = "SAM SA-18 Igla-S comm", -- 53 [IglaGRG_1] = "SAM SA-18 Igla MANPADS", -- 54 [IglaGRG_2] = "SAM SA-18 Igla comm", -- 55 [StingerUSA_1] = "SAM Stinger MANPADS", -- 56 [StingerUSA_2] = "SAM Stinger comm", -- 57 [74] = "SAM SA-3 S-125 LN 5P73", -- 74 (SA3_LN is not defined in Black Shark) }, [wsType_Radar]= -- 101 { [73] = "SAM SA-3 S-125 TR SNR", -- 73 (SA3_TR is not defined in Black Shark) [75] = "SAM SA-3 S-125 SR P-19", -- 75 (SA3_SR is not defined in Black Shark) }, }, [wsType_Tank]= -- 17 { [wsType_Gun]= -- 26 { [93] = "Infantry", -- 93 (Infantry M249 / Infantry M4) [95] = "ARV MTLB-U BOMAN", -- 95 (wsTypeBOMAN) [96] = "Bunker", -- 96 (wsTypeSandbox) [97] = "Bunker", -- 97 (wsTypeBunker) }, [wsType_MissGun]= -- 104 { [94] = "Infantry RPG-7", -- 94 (wsTypeSoldier_RPG) }, }, }, [wsType_Static]= -- 5 { [wsType_Airdrome]= -- 13 { [wsType_Heliport]= -- 40 { [Heliport_standart] = "Heliport", -- 100 }, }, }, }, -- Convert Lock-On Coalition_ID To Tacview Coalition_ID CoalitionLookupTable= { ["Allies"] = 0, ["Enemies"] = 1, }, -- Convert Lock-On Country_Name To ISO Country_Code -- \LockOn Flaming Cliffs 2\Scripts\World\Countries.lua CountryCodeLookupTable= { [ 0] = "ru", [ 1] = "ua", [ 2] = "us", [ 3] = "tr", [ 4] = "uk", [ 5] = "fr", [ 6] = "de", -- [ 7] = "", [ 8] = "ca", [ 9] = "es", [10] = "nl", [11] = "be", [12] = "no", [13] = "dk", -- [14] = "", [15] = "il", [16] = "ge", [17] = "xi", -- [Insurgents] (free for use ISO code) [18] = "xa", -- Abkhazia [19] = "xo", -- South Osetia }, -- Fast File Class (lazy write cache) FastFile= { -- Constants MaxCacheSize=64*1024, -- Maximum data in cache before automatic flush -- Create file Create=function(self,FileName) -- Check Parameters if self.File then self:Close() end -- Create a new file self.File=io.open(FileName,"wb"); if not self.File then return false; -- Operation failed end -- Complete return true; end, -- Inject data into file (lazy write) Write=function(self,Data) -- Check Parameters if not self.File then return false; -- File not open end -- Update Cache if self.CacheSize+string.len(Data)>=self.MaxCacheSize then -- Add Part Of Data To Cache if self.CacheSize=self.NextDefaultObjectsUpdateTime then ShouldUpdateLog=true; self.NextDefaultObjectsUpdateTime=CurrentTime+self.DefaultObjectsUpdatePeriod; else ShouldUpdateLog=false; end local DefaultObjectsList=LoGetWorldObjects(); -- LoGetWorldObjects("units"); if self:AddUpdateObjects(DefaultObjectsList,FormatedTime,ShouldUpdateLog)==true then FormatedTime=nil; end -- Add/Update ballistic objects if CurrentTime>=self.NextBallisticObjectsUpdateTime then ShouldUpdateLog=true; self.NextBallisticObjectsUpdateTime=CurrentTime+self.BallisticObjectsUpdatePeriod; else ShouldUpdateLog=false; end local BallisticObjectsList=LoGetWorldObjects("ballistic"); if self:AddUpdateObjects(BallisticObjectsList,FormatedTime,ShouldUpdateLog)==true then FormatedTime=nil; end -- Remove destroyed objects self:RemoveObjects(DefaultObjectsList,BallisticObjectsList,FormatedTime); end, -- Add/Update objects AddUpdateObjects=function(self,CurrentObjectsList,FormatedTime,ShouldUpdateLog) -- Check parameters if not CurrentObjectsList then return false; end -- Dump objects local LogWasUpdated=false; for ID,Object in pairs(CurrentObjectsList) do -- Check Object Type local ObjectType=self.ConvertObjectType(Object.Type); if ObjectType~=-1 then -- Ignore some objects (like particles clouds) -- Add new object if not self.LastObjectsStatus[ID] then local LogLine; -- Time prefix if FormatedTime then LogLine=FormatedTime; FormatedTime=nil; else LogLine=""; end -- Object/Pilot Name local ObjectName=Object.Name; local PilotName; if Object.UnitName then PilotName=Object.UnitName; else PilotName=""; end -- Object Type local ObjectTypeFormated; if ObjectType and type(ObjectType)=="number" then ObjectTypeFormated=string.format("%x",ObjectType); else -- This will help to debug unknown types ObjectTypeFormated=string.format("%s.%s.%s.%s",Object.Type.level1,Object.Type.level2,Object.Type.level3,Object.Type.level4); end -- Coalition local CoalitionID; if ObjectType==0x2e or self.IsSimpleObject(ObjectType)==true then CoalitionID="?"; -- Chaff/Flare/Shell/Parachutist coalition is not reliable in DCS else if Object.Coalition then CoalitionID=self.CoalitionLookupTable[Object.Coalition]; if not CoalitionID then CoalitionID="?"; end else CoalitionID="?"; end end -- Country local CountryCode; if Object.Country then CountryCode=self.CountryCodeLookupTable[Object.Country]; if not CountryCode then CountryCode="?"; end else CountryCode="?"; end -- Group Name local GroupName; if Object.GroupName then GroupName=Object.GroupName; else GroupName="?"; end -- Debug Invalid Names if not ObjectName or ObjectName=="" then -- If object name is invalid if self.IsSimpleObject(ObjectType)==false and ObjectType~=0x46 then -- and this is not a Shell/Chaff/Flare ObjectName=self.FixInvalidObjectName(Object.Type,GroupName); end end -- Reduce ID size (to compact text log) local OptimizedID=self.GetOptimizedID(ID); -- Declare new object self.AcmiFile:Write(LogLine..string.format("+%x,?,%s,%s,%s,%s,%s,%s,?\n",OptimizedID,ObjectTypeFormated,CoalitionID,CountryCode,self.CleanupName(ObjectName),self.CleanupName(PilotName),self.CleanupName(GroupName))); -- Done LogWasUpdated=true; end -- Update object data as required if self:UpdateObject(ID,Object,ObjectType,self.LoggedObjectsStatus[ID],FormatedTime,ShouldUpdateLog)==true then FormatedTime=nil; LogWasUpdated=true; end end end -- Completed return LogWasUpdated; end, -- Fix Yaw (because FC/BS map is a 2D projection, not a true 3D sphere) FixedYaw=function(Yaw,RefX,RefZ,Latitude,Longitude) -- Calculate vector to north in Lock-On coordinates local ToNorthPos=LoGeoCoordinatesToLoCoordinates(Longitude,Latitude+1); local ToNorthX=ToNorthPos.z-RefZ; local ToNorthY=ToNorthPos.x-RefX; local ToNorthLength=math.sqrt(ToNorthX*ToNorthX+ToNorthY*ToNorthY); -- Normalize vector if ToNorthLength>0 then ToNorthX=ToNorthX/ToNorthLength; ToNorthY=ToNorthY/ToNorthLength; end -- Calculate Yaw Error return Yaw+math.atan2(ToNorthY,ToNorthX)-math.pi/2; end, -- Update one object UpdateObject=function(self,ID,CurrentObjectData,ObjectType,PrevObjectData,FormatedTime,ShouldUpdateLog) -- Log object dynamic properties local ChangeDetected=false; local LogWasUpdated=false; local Log=""; -- Latitude if not PrevObjectData or CurrentObjectData.LatLongAlt.Lat~=PrevObjectData.LatLongAlt.Lat then Log=Log..string.format(",%.6f",CurrentObjectData.LatLongAlt.Lat-self.LatitudeOffset); ChangeDetected=true; else Log=Log..","; end -- Longitude if not PrevObjectData or CurrentObjectData.LatLongAlt.Long~=PrevObjectData.LatLongAlt.Long then Log=Log..string.format(",%.6f",CurrentObjectData.LatLongAlt.Long-self.LongitudeOffset); ChangeDetected=true; else Log=Log..","; end -- Altitude if not PrevObjectData or CurrentObjectData.LatLongAlt.Alt~=PrevObjectData.LatLongAlt.Alt then Log=Log..string.format(",%.2f",CurrentObjectData.LatLongAlt.Alt); ChangeDetected=true; else Log=Log..","; end -- Roll/Pitch/Yaw if self.IsSimpleObject(ObjectType)==true then -- shell/bullet/ballistic-shell/flare/chaff/smoke-grenade/cartridge -- Do not log roll/pitch/yaw to reduce recording size Log=Log.."\n"; elseif ObjectType==0x46 or (ObjectType==0x4c and CurrentObjectData.Name=="") then -- bombs without names => bomblets -- Emulate bomblets and shrapnel orientation if not PrevObjectData then Log=Log..",?,?,?\n"; else Log=Log.."\n"; end else -- Roll if not PrevObjectData or CurrentObjectData.Bank~=PrevObjectData.Bank then local NewFormatedRoll=string.format(",%.1f",math.mod(math.deg(CurrentObjectData.Bank),360)); local RollIsDifferent=true; if PrevObjectData then local OldFormatedRoll=string.format(",%.1f",math.mod(math.deg(PrevObjectData.Bank),360)); if NewFormatedRoll==OldFormatedRoll then RollIsDifferent=false; end end if RollIsDifferent==true then Log=Log..NewFormatedRoll; ChangeDetected=true; else Log=Log..","; -- not different enough to be dumped end else Log=Log..","; end -- Pitch if not PrevObjectData or CurrentObjectData.Pitch~=PrevObjectData.Pitch then local NewFormatedPitch=string.format(",%.1f",-math.mod(math.deg(-CurrentObjectData.Pitch),360)); local PitchIsDifferent=true; if PrevObjectData then local OldFormatedPitch=string.format(",%.1f",-math.mod(math.deg(-PrevObjectData.Pitch),360)); if NewFormatedPitch==OldFormatedPitch then PitchIsDifferent=false; end end if PitchIsDifferent==true then Log=Log..NewFormatedPitch; ChangeDetected=true; else Log=Log..","; -- not different enough to be dumped end else Log=Log..","; end -- Yaw if not PrevObjectData or CurrentObjectData.Heading~=PrevObjectData.Heading then local NewFormatedYaw=string.format(",%.1f\n",math.mod(math.deg(Tacview.FixedYaw(CurrentObjectData.Heading,CurrentObjectData.Position.x,CurrentObjectData.Position.z,CurrentObjectData.LatLongAlt.Lat,CurrentObjectData.LatLongAlt.Long)),360)); local YawIsDifferent=true; if PrevObjectData then local OldFormatedYaw=string.format(",%.1f\n",math.mod(math.deg(Tacview.FixedYaw(PrevObjectData.Heading,PrevObjectData.Position.x,PrevObjectData.Position.z,PrevObjectData.LatLongAlt.Lat,PrevObjectData.LatLongAlt.Long)),360)); if NewFormatedYaw==OldFormatedYaw then YawIsDifferent=false; end end if YawIsDifferent==true then Log=Log..NewFormatedYaw; ChangeDetected=true; else Log=Log..",\n"; -- not different enough to be dumped end else Log=Log..",\n"; end end -- Log data if not PrevObjectData or ( ChangeDetected==true and ShouldUpdateLog==true ) then if FormatedTime then self.AcmiFile:Write(FormatedTime); end local OptimizedID=self.GetOptimizedID(ID); self.AcmiFile:Write(string.format("%x",OptimizedID)..Log); LogWasUpdated=true; -- Remember Last Logged Object Properties self.LoggedObjectsStatus[ID]=CurrentObjectData; end -- Remember Current Frame Object Properties self.LastObjectsStatus[ID]=CurrentObjectData; -- Complete return LogWasUpdated; end, -- Remove any destroyed objects RemoveObjects=function(self,DefaultObjectsList,BallisticObjectsList,FormatedTime) local Log; if not DefaultObjectsList then DefaultObjectsList={}; end if not BallisticObjectsList then BallisticObjectsList={}; end for ID,Object in pairs(self.LastObjectsStatus) do if (not DefaultObjectsList[ID]) and (not BallisticObjectsList[ID]) then -- Log last position if required local ObjectType=self.ConvertObjectType(Object.Type); if self:UpdateObject(ID,Object,ObjectType,self.LoggedObjectsStatus[ID],FormatedTime,true)==true then FormatedTime=nil; end -- Prefix event with time if required if FormatedTime then Log=FormatedTime; FormatedTime=nil; else Log=""; end -- Log Event self.AcmiFile:Write(Log..string.format("!20,%x\n",self.GetOptimizedID(ID))); -- Remove object from lists self.LoggedObjectsStatus[ID]=nil; self.LastObjectsStatus[ID]=nil; end end end, -- Stop ACMI logging EndLog=function(self) if self.AcmiFile then self.AcmiFile:Close(); self.AcmiFile=nil; end end, -- Files Tools FileExist=function(FileName) local FileToTest=io.open(FileName); if FileToTest then io.close(FileToTest); return true; end return false; end, -- Export One Sector in SRTM3 text format ExportSector=function(Latitude,Longitude) local LatitudeName; if Latitude>=0 then LatitudeName=string.format("N%02u",Latitude); else LatitudeName=string.format("S%02u",-Latitude); end local LongitudeName; if Longitude>=0 then LongitudeName=string.format("E%03u",Longitude); else LongitudeName=string.format("W%03u",-Longitude); end local TerrainFile=io.open("./Temp/"..LatitudeName..LongitudeName..".hgt.txt","wb"); for y=1200,0,-1 do local CurrentLatitude=Latitude+y/1200; for x=0,1200 do local CurrentLongitude=Longitude+x/1200; local DCSCoordinates=LoGeoCoordinatesToLoCoordinates(CurrentLongitude,CurrentLatitude); TerrainFile:write(LoGetAltitude(DCSCoordinates.x,DCSCoordinates.z).."\n"); end end io.close(TerrainFile); end, -- Export Whole Terrain ExportTerrain=function(self) -- ge (Black Shark) self.ExportSector(40,40); self.ExportSector(40,41); self.ExportSector(40,42); self.ExportSector(40,43); self.ExportSector(40,44); self.ExportSector(41,40); self.ExportSector(41,41); self.ExportSector(41,42); self.ExportSector(41,43); self.ExportSector(41,44); self.ExportSector(42,40); self.ExportSector(42,41); self.ExportSector(42,42); self.ExportSector(42,43); self.ExportSector(42,44); -- ge (A-10C Warthog) self.ExportSector(40,45); self.ExportSector(41,45); self.ExportSector(42,45); self.ExportSector(43,45); -- ru (Black Shark) self.ExportSector(43,39); self.ExportSector(43,40); self.ExportSector(43,41); self.ExportSector(43,42); self.ExportSector(43,43); self.ExportSector(43,44); self.ExportSector(44,37); self.ExportSector(44,38); self.ExportSector(44,39); self.ExportSector(44,40); self.ExportSector(44,41); self.ExportSector(44,42); self.ExportSector(44,43); self.ExportSector(44,44); self.ExportSector(45,37); self.ExportSector(45,38); self.ExportSector(45,39); self.ExportSector(45,40); --[[ -- ua (Flaming Cliffs) self.ExportSector(44,33); self.ExportSector(44,34); self.ExportSector(44,35); self.ExportSector(45,32); self.ExportSector(45,33); self.ExportSector(45,34); self.ExportSector(45,35); self.ExportSector(45,36); -- us (A-10C Warthog) self.ExportSector(35,-115); self.ExportSector(35,-116); self.ExportSector(35,-117); self.ExportSector(36,-115); self.ExportSector(36,-116); self.ExportSector(36,-117); self.ExportSector(37,-115); self.ExportSector(37,-116); self.ExportSector(37,-117); ]]-- end, -- Dump Table Content table_val_to_str=function(self,v) if "string" == type( v ) then v = string.gsub( v, "\n", "\\n" ) if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then return "'" .. v .. "'" end return '"' .. string.gsub(v,'"', '\\"' ) .. '"' end return "table" == type( v ) and self:table_tostring( v ) or tostring( v ) end, table_key_to_str=function(self,k) if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then return k end return "[" .. self:table_val_to_str( k ) .. "]" end, table_tostring=function(self,tbl) local result, done = {}, {} for k, v in ipairs( tbl ) do table.insert( result, self:table_val_to_str( v ) ) done[ k ] = true end for k, v in pairs( tbl ) do if not done[ k ] then table.insert( result, self:table_key_to_str( k ).."="..self:table_val_to_str( v ) ) end end return "{" .. table.concat( result, "," ) .. "}" end, -- Dump Global Variables SeenVariable={}, DumpVariables=function(self,dumpfile,t,i) self.SeenVariable[t]=true local s={} local n=0 for k in pairs(t) do n=n+1 s[n]=k end table.sort(s) for k,v in ipairs(s) do if v~="SeenVariable" then dumpfile:write(i,v); v=t[v]; dumpfile:write(" = ",tostring(v),"\n"); if type(v)=="table" and not self.SeenVariable[v] then self:DumpVariables(dumpfile,v,i.."\t"); end end end end, DumpGlobalVariables=function(self) local DumpFile=io.open("./Temp/GlobalVariablesDump.txt","wb"); self:DumpVariables(DumpFile,_G,"") io.close(DumpFile); end, } -- (Hook) Works once just before mission start. do local PrevLuaExportStart=LuaExportStart; LuaExportStart=function() -- Tacview:DumpGlobalVariables(); -- Tacview:ExportTerrain(); Tacview:BeginLog(); if PrevLuaExportStart then PrevLuaExportStart(); end end end -- (Hook) Works just before every simulation frame. do local PrevLuaExportBeforeNextFrame=LuaExportBeforeNextFrame; LuaExportBeforeNextFrame=function() -- Tacview:UpdateLog(); if PrevLuaExportBeforeNextFrame then PrevLuaExportBeforeNextFrame(); end end end -- (Hook) Works just after every simulation frame. do local PrevLuaExportAfterNextFrame=LuaExportAfterNextFrame; LuaExportAfterNextFrame=function() Tacview:UpdateLog(); if PrevLuaExportAfterNextFrame then PrevLuaExportAfterNextFrame(); end end end -- (Hook) Works once just after mission stop. do local PrevLuaExportStop=LuaExportStop; LuaExportStop=function() Tacview:EndLog(); if PrevLuaExportStop then PrevLuaExportStop(); end end end