UI = {} UI.Version = '1.3.03' --################################################################################################################################################### -- ############# DCS - Range Control User Interface for Target Impact Tracker Script 2.0 ################# -- ############# by Draken35 ################# --################################################################################################################################################### --[[ Requirements: >>> MIST 4.5.107 or above >>> TITS 2.3.00 or above --]] ----------------------------------------------------------------------------------------------------------------------------------------------------- --[[ Version history 1.0.00 04/06/2022 Development starts 1.0.01 05/03/2022 First release 1.0.02 05/08/2022 -fixed Apache automatic coordinates system detection 1.1.00 05/10/2022 - Uses TITS 2.1.x results API to report results instead of raw data tables 1.1.01 05/11/2022 - Fixed issue with popup script and new result reports - replaced UI.MaxWFindRPT by I.MinWFgrpRPT 1.1.02 05/12/2022 - Added optional abort message when shooting before starting a pass 1.1.10 05/14/2022 - No mark designation will display the UI.Text1,UI.Text2 and UI.Text3, so they can be used to give a talk-on for the target. - Coordinates are now optional in the No-mark and laser/IR designation designation. Toggle option in designation menu 1.2.00 05/15/2022 - Reorganization of Setting Menu - Added optional Auto Pass Start and End 1.2.01 05/18/2022 - Attack Radial added to designation - Added release heading to reports - Added reports based on designated target - Fixed and readded Pass BDA report 1.2.02 05/25/2022 - Fixed call to designated/grouped report from menu 1.2.03 07/10/2022 - Range comms improved for MP games - display on coordenates in 3 systems at the same time 1.2.04 07/22/2022 - Fixed a crash in IR designation 1.3.00 07/27/2022 - Added support for roll-in position tracking - Added illumination zones 1.3.01 07/30/2022 - Added talk-on fields to the laser/IR designation 1.3.02 08/04/2022 - improved initialization of Auto pass start and end 1.3.03 09/11/2022 - Lest we forget! - Fixed issue with strafing reporting in missions with unlimited weapons. A limitation is that it will only report passed with hits on target and it cannot report rate of hits.\ - Fixed issue with automatic range clearance trigger for AI units spawned inside the zones --]] ----------------------------------------------------------------------------------------------------------------------------------------------------- --[[ Pass Data return tables Tables: shellsFired[ShellType] = { init = Shell Count at pass start , fired = Shells fired during the pass } shellHits -- per target per shell type shellHits[Target][ShellType] = Shell Hits Count weaponsFired -- and their release params & designated target at the time, updated by shot event if TITS.onPass[_unitName] == true weaponsFired[WeaponID] = { weaponType , releasePitch , releaseYaw , releaseRoll , releaseHeading , releaseSpeed , releasePosition , timeStamp , inFlight } weaponHits -- per target per weapon , updated by hit event if TITS.onPass[_unitName] == true weaponHits[WeaponID][Target] = timeStamp impactData -- per target per weapon impactData[WeaponID][Target] = { targetPos , impactPos , impactDistance , timeStamp } targetHealth -- per target. Health at the start and end of the pass targetHealth[Target] = { start_health , end_health } --]] --[[ Target List table TargetList[name] = { name -- UNIQUE! Target name , displayName -- display name, accepts duplicates, if empty _TargetName will be used , type -- allowed values [ 'unit' | 'zone' | 'static' ] must be in lower case , respawn -- Only for units. if true, unit can be respawned. Respwan will only happen when ALL the units in the group are destroyed , impact -- use target to calculate distance to impact , strafing -- count strafing hits on target , bda -- reports splash damage from weapons , des_zone -- designation zone , des_wp -- Allow use of smoke designation for target , des_laser -- Allow use of laser designation for target , des_ir -- Allow use of IR designation for target , des_nomark -- Allow use of target designation with no marks (provides coordinates) , list_coor -- Allow list of target coordinates , uiNum1 -- Numeric value for custom UI use , uiNum2 -- Numeric value for custom UI use , uiNum3 -- Numeric value for custom UI use , uiBool1 -- Boolean value for custom UI use , uiBool2 -- Boolean value for custom UI use , uiBool3 -- Boolean value for custom UI use , uiText1 -- Text value for custom UI use , uiText2 -- Text value for custom UI use , uiText3 -- Text value for custom UI use } --]] --################################################################################################################################################### -- ############# Configuration section ################# --################################################################################################################################################### UI.AutoPassStart = false UI.AutoPassStartCoolDown = 30 -- seconds UI.AutoPassEndTimeOut = 20 -- seconds UI.AutoPassEnd = false -- If true, pass will automatically end if there aren't any weapons in the air and -- UI.AutoPassEndTimeOut seconds had passed since the last shell was fired or weapon impact -- was recorded UI.MagneticVar = 0 -- Magnetic variation for the map and date, used for designation attack radial calculation. East is + UI.SpeedUnitsDefault = 1 -- 1 = Knots, 2 = Mph, 3 = Kph UI.DistanceUnitsDefault = 3 -- 1 = feet, 2 = yards, 3 = meters UI.AltitudeUnitsDefault = 1 -- 1 = feet, 3 = meters ( 2 is not used) UI.CoordinateFormatDefault = 2 -- 1 = MGRS, 2 = DMS, 3 = DM.mm UI.DisplayReleaseDataDefault = true -- default for the option to display the release parameters in results UI.DisplayAbortMsgDefault = true -- if true, display abort message when shooting before starting a pass UI.DisplayCoordsDefault = true -- if true, display coordinates in designation messages UI.desUseAttackRadialDefault = false -- add an attack radial requiment if true UI.DefaultResultReport = 0 -- 0 = Automatic -- 1 = Impact results (closest/individual) -- 2 = Impact results (closest/grouped) -- 3 = BDA Summary -- 4 = Impact results (designated/individual) UI.MinWFgrpRPT = 6 -- Minimun number of weapons fired in pass to display grouped report in automatic mode UI.minWPmarkDist = 50 -- meters. Minimum distance from target to mark using WP UI.maxWPmarkDist = 150 -- meters. Maximum distance from target to mark using WP UI.AutoSetCoordinates = true -- if true use player's unit type to set coordinates, else use MPT.CoordinateFormatDefault UI.coordXtype = {} UI.coordXtype['AV8BNA'] = 1 -- 1 = MGRS, 2 = DMS, 3 = DM.mm UI.coordXtype['A-10C_2'] = 1 UI.coordXtype['AH-64D_BLK_II'] = 1 UI.coordXtype['F-16C_50'] = 3 UI.coordXtype['FA-18C_hornet'] = 1 UI.coordXtype['M-2000C'] = 3 UI.messageBeep = '204521__redoper__roger-beep.ogg' -- Message alert sound. leave empty for no sound --################################################################################################################################################# -- ############# Initialization of globals ############### --################################################################################################################################################# UI.unitConfig = {} UI.menuAddedToGroup = {} UI.debugMarkcenter = false UI.DesignationZones = {} UI.DesignationZones[1] = { id = 1, name = 'Default'} UI.Designation = { Designator = '' -- unit name , Designated = '' -- target name , type = 0 -- 0 = none, 1 = WP , 2 = laser, 3 = IR, 4 = No mark, 23 = laser/IR , zone = 1 , briefing = '' , ray1 = nil , ray2 = nil , AttackRadial = 0 } UI.Designators = {} UI.engagementZones = {} UI.illuminationZones = {} UI.illuminating = false --################################################################################################################################################# -- ############# UI code ############### --################################################################################################################################################# UI.EventHandler = {} function UI.EventHandler:onEvent(_eventDCS) if _eventDCS == nil or _eventDCS.initiator == nil then return true end local status, err = pcall(function(_event) if (_event.id == world.event.S_EVENT_BIRTH or _event.id == world.event.S_EVENT_TAKEOFF or _event.id == world.event.S_EVENT_ENGINE_STARTUP) and _event.initiator:getPlayerName() then UI.initUnit(_event.initiator:getName()) elseif ((_event.id == world.event.S_EVENT_SHOT) or (_event.id == world.event.S_EVENT_SHOOTING_START) ) and _event.initiator:getPlayerName() then -- id == 1 Shot local _unitName = _event.initiator:getName() local cfg = UI.unitConfig[_unitName] if not TITS.onPass[_unitName] and cfg.abortMSG then UI.messageToUnit(_unitName,"Abort! You are not cleared to fired!",{},10) end end --_event.id == -- ### End event processing return true end, _eventDCS) -- local status, err = pcall(function(_event) if (not status) then local msg = string.format("Target Impact Tracker UI (v%s): Error while handling event %s",UI.Version, err) env.error(msg,false) end end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.initUnit(unitName) local unit = Unit.getByName(unitName) local groupID = unit:getGroup():getID() local rConfig = { SpeedUOM = UI.SpeedUnitsDefault , DistanceUOM = UI.DistanceUnitsDefault , AltitudeUOM = UI.AltitudeUnitsDefault , CoordFormat = UI.CoordinateFormatDefault , DisplayReleaseData = UI.DisplayReleaseDataDefault , rootPath = nil , rollInPath = nil , offPath = nil , resultsRPT = UI.DefaultResultReport , unitType = unit:getTypeName() , abortMSG = UI.DisplayAbortMsgDefault , desDisplayCoords = UI.DisplayCoordsDefault , desUseAttackRadial = UI.desUseAttackRadialDefault , lastPassEnd = timer.getTime() , firstPass = true } if UI.coordXtype[rConfig.unitType] and UI.AutoSetCoordinates then rConfig.CoordFormat = UI.coordXtype[rConfig.unitType] end if UI.menuAddedToGroup[groupID] == {} then UI.menuAddedToGroup[groupID] = false end if not UI.menuAddedToGroup[groupID] then -- Add Menu rConfig.rootPath = missionCommands.addSubMenuForGroup(groupID, "Range") -- Root level local _reportsPath = missionCommands.addSubMenuForGroup(groupID,"Reports", rConfig.rootPath) missionCommands.addCommandForGroup(groupID,"List coordinates" , _reportsPath, UI.rptListCoord , unitName) missionCommands.addCommandForGroup(groupID,"Results (closest/individual)" , _reportsPath, UI.rptImpactResults , unitName) missionCommands.addCommandForGroup(groupID,"Results (closest/grouped)" , _reportsPath, UI.rptImpactResultsGrouped , unitName) missionCommands.addCommandForGroup(groupID,"Results (designated/individual)" , _reportsPath, UI.rptImpactResultsDes , unitName) missionCommands.addCommandForGroup(groupID,"Results (designated/grouped)" , _reportsPath, UI.rptImpactResultsDesGrouped , unitName) missionCommands.addCommandForGroup(groupID,"BDA Pass summary" , _reportsPath, UI.rptBDAsummary , unitName) missionCommands.addCommandForGroup(groupID,"Weapons in flight" , _reportsPath, UI.rptWeaponsInFlight , unitName) --missionCommands.addCommandForGroup(groupID,"Debug: Inspect Data" , _reportsPath, UI.debugPassData , unitName) local _designationPath = missionCommands.addSubMenuForGroup(groupID, "Designation", rConfig.rootPath) missionCommands.addCommandForGroup(groupID,"New smoke (WP) designation" , _designationPath, UI.desWP , {unitName, true}) missionCommands.addCommandForGroup(groupID,"Repeat smoke (WP) mark" , _designationPath, UI.desWP , {unitName, false}) missionCommands.addCommandForGroup(groupID,"New Laser designation" , _designationPath, UI.desLaserIR , {unitName,'laser'}) missionCommands.addCommandForGroup(groupID,"New IR pointer designation" , _designationPath, UI.desLaserIR , {unitName,'IR'}) missionCommands.addCommandForGroup(groupID,"New Laser/IR designation" , _designationPath, UI.desLaserIR , {unitName,'laser/IR'}) missionCommands.addCommandForGroup(groupID,"New No Mark designation" , _designationPath, UI.desNM , unitName) missionCommands.addCommandForGroup(groupID,"Cancel designation" , _designationPath, UI.desCancel , unitName) missionCommands.addCommandForGroup(groupID,"Repeat briefing" , _designationPath, UI.desRPTBrief , unitName) ---------------------------------------------------------------------------------------- local _settingsPath = missionCommands.addSubMenuForGroup(groupID, "Settings", rConfig.rootPath) missionCommands.addCommandForGroup(groupID,"Display current settings" , _settingsPath, UI.Settings , {unitName,0}) missionCommands.addCommandForGroup(groupID,"Toggle illumination" , _settingsPath, UI.Settings , {unitName,70}) missionCommands.addCommandForGroup(groupID,"Respawn targets" , _settingsPath, UI.Settings , {unitName,66}) missionCommands.addCommandForGroup(groupID,"Toggle abort message" , _settingsPath, UI.Settings , {unitName,1}) missionCommands.addCommandForGroup(groupID,"Toggle display of Release data" , _settingsPath, UI.Settings , {unitName,2}) local _settings6Path = missionCommands.addSubMenuForGroup(groupID, "Designation", _settingsPath) local _designation1Path = missionCommands.addSubMenuForGroup(groupID, "Select designation zone", _settings6Path) for _,z in pairs(UI.DesignationZones) do local n = string.format('%s', z.name) missionCommands.addCommandForGroup(groupID,n , _designation1Path, UI.desSetZone , {unitName,z.id}) end missionCommands.addCommandForGroup(groupID,"Toggle attack radial" , _settings6Path, UI.Settings , {unitName,31}) missionCommands.addCommandForGroup(groupID,"Toggle coods. display" , _settings6Path, UI.Settings , {unitName,30}) local _settings1Path = missionCommands.addSubMenuForGroup(groupID, "Distance units", _settingsPath) missionCommands.addCommandForGroup(groupID,"Distance: set to meters" , _settings1Path, UI.Settings , {unitName,3}) missionCommands.addCommandForGroup(groupID,"Distance: set to feet" , _settings1Path, UI.Settings , {unitName,4}) missionCommands.addCommandForGroup(groupID,"Distance: set to yards" , _settings1Path, UI.Settings , {unitName,5}) local _settings2Path = missionCommands.addSubMenuForGroup(groupID, "Altitude units", _settingsPath) missionCommands.addCommandForGroup(groupID,"Altitude: set to meters" , _settings2Path, UI.Settings , {unitName,6}) missionCommands.addCommandForGroup(groupID,"Altitude: set to feet" , _settings2Path, UI.Settings , {unitName,7}) local _settings3Path = missionCommands.addSubMenuForGroup(groupID, "Speed units", _settingsPath) missionCommands.addCommandForGroup(groupID,"Speed: set to kph" , _settings3Path, UI.Settings , {unitName,8}) missionCommands.addCommandForGroup(groupID,"Speed: set to knots" , _settings3Path, UI.Settings , {unitName,9}) missionCommands.addCommandForGroup(groupID,"Speed: set to mph" , _settings3Path, UI.Settings , {unitName,10}) --local _settings4Path = missionCommands.addSubMenuForGroup(groupID, "Coordinates format", _settingsPath) -- missionCommands.addCommandForGroup(groupID,"Coord. format set to MGRS" , _settings4Path, UI.Settings , {unitName,11}) -- missionCommands.addCommandForGroup(groupID,"Coord. format set to DMS" , _settings4Path, UI.Settings , {unitName,12}) -- missionCommands.addCommandForGroup(groupID,"Coord. format set to DM.mm" , _settings4Path, UI.Settings , {unitName,13}) local _settings5Path = missionCommands.addSubMenuForGroup(groupID, "Results report", _settingsPath) missionCommands.addCommandForGroup(groupID,"Automatic selection" , _settings5Path, UI.Settings , {unitName,20}) missionCommands.addCommandForGroup(groupID,"Impact results (individual)" , _settings5Path, UI.Settings , {unitName,21}) missionCommands.addCommandForGroup(groupID,"Impact results (grouped)" , _settings5Path, UI.Settings , {unitName,22}) missionCommands.addCommandForGroup(groupID,"BDA Pass summary" , _settings5Path, UI.Settings , {unitName,23}) ---------------------------------------------------------------------------------------- rConfig.rollInPath = missionCommands.addCommandForGroup(groupID,"Rolling in" , rConfig.rootPath , UI.passStartEnd, {unitName,'in'}) UI.menuAddedToGroup[groupID] = true UI.unitConfig[unitName] = rConfig UI.Settings({unitName,0}) if UI.AutoPassStart then -- launch pass end checker local params = {unitName = unitName} timer.scheduleFunction(UI.passStartCheck, params, timer.getTime() + 1 ) end if UI.AutoPassEnd then -- launch pass end checker local params = {unitName = unitName} timer.scheduleFunction(UI.passEndCheck, params, timer.getTime() + 1 ) end end --if not UI.menuAddedToGroup[groupID] then return true end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.passStartCheck(params, time) if not TITS.onPass[params.unitName] then if UI.rowCount(UI.engagementZones) > 0 then local inZone = false for z,t in pairs(UI.engagementZones) do local ut = mist.makeUnitTable({params.unitName}) if t == 'zone' then local u = mist.getUnitsInZones(ut, {z},'cylinder') inZone = inZone or (UI.rowCount(u) > 0) else local p = mist.getGroupPoints(z) local u = mist.getUnitsInPolygon(ut, p) inZone = inZone or (UI.rowCount(u) > 0) end end -- for z,t in pairs(UI.engagementZones) do if inZone then local cfg = UI.unitConfig[params.unitName] if (timer.getTime() - cfg.lastPassEnd ) >= UI.AutoPassStartCoolDown or cfg.firstPass then cfg.firstPass = false UI.passStartEnd({params.unitName, 'in'}) end end end --if UI.rowCount(UI.engagementZones) > 0 then end -- if not TITS.onPass[params.unitName] then return timer.getTime() + 1 end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.passEndCheck(params, time) if TITS.onPass[params.unitName] then local passData = TITS.passGetData(params.unitName) local wf = TITS.getWeponsFired(params.unitName) local sf = TITS.getShellsFired(params.unitName) if wf.count > 0 or sf.total > 0 then if not TITS.weaponsInFlight(params.unitName) then local cTime = timer.getTime() if (cTime - passData.lastShellFired) > UI.AutoPassEndTimeOut and (cTime - passData.lastImpact) > UI.AutoPassEndTimeOut then UI.passStartEnd({params.unitName, 'off'}) end -- if (cTime - passData.lastShellFired) > UI.AutoPassEndTimeOut and end -- if not TITS.weaponsInFlight(params.unitName) then end -- if wf.count > 0 or sf.count > 0 then end -- if TITS.onPass[params.unitName] then return timer.getTime() + 1 end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.AddEngagementZone(_name, _type) UI.engagementZones[_name] = _type end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.Settings(_args) local unitName = _args[1] local setting = _args[2] local cfg = UI.unitConfig[unitName] if setting == 0 then -- Display settings local _msg = '' _msg = string.format ('Flying: %s',cfg.unitType) if cfg.abortMSG then _msg = string.format ('%s\nAbort message is ON',_msg) else _msg = string.format ('%s\nAbort message is OFF',_msg) end if cfg.DisplayReleaseData then _msg = string.format ('%s\nRelease data display is ON',_msg) else _msg = string.format ('%s\nRelease data display is OFF',_msg) end if cfg.DistanceUOM == 3 then -- Distance meters _msg = string.format ('%s\nDistance: Using meters',_msg) elseif cfg.DistanceUOM == 2 then --Distance feet _msg = string.format ('%s\nDistance: Using feet',_msg) else -- Distance yards _msg = string.format ('%s\nDistance: Using yards',_msg) end if cfg.AltitudeUOM == 3 then -- Altitude meters _msg = string.format ('%s\nAltitude: Using meters',_msg) else _msg = string.format ('%s\nAltitude: Using feet',_msg) end if cfg.SpeedUOM == 3 then -- Speed kph _msg = string.format ('%s\nSpeed: Using kph',_msg) elseif cfg.SpeedUOM == 1 then -- Speed kt _msg = string.format ('%s\nSpeed: Using knots',_msg) else -- Speed mph _msg = string.format ('%s\nSpeed: Using mph',_msg) end --[[ if cfg.CoordFormat == 1 then -- Coordinate format MGRS _msg = string.format ('%s\nCoordinate format: Using MGRS',_msg) elseif cfg.CoordFormat == 2 then -- Coordinate format DMS _msg = string.format ('%s\nCoordinate format: Using DMS',_msg) else -- Coordinate format DM.mm _msg = string.format ('%s\nCoordinate format: Using DM.mm',_msg) end --]] if cfg.resultsRPT == 0 then _msg = string.format ('%s\nResults using Automatic selection',_msg) elseif cfg.resultsRPT == 1 then -- _msg = string.format ('%s\nResults using Impact results (individual) report',_msg) elseif cfg.resultsRPT == 2 then _msg = string.format ('%s\nResults using Impact results (grouped) report',_msg) elseif cfg.resultsRPT == 3 then _msg = string.format ('%s\nResults using BDA Pass Summary report',_msg) end _msg = string.format ('%s\nDesignation zone: %s',_msg,UI.DesignationZones[UI.Designation.zone].name) if cfg.desDisplayCoords then _msg = string.format ('%s\nDisplay coordinates in designation msgs is ON',_msg) else _msg = string.format ('%s\nDisplay coordinates in designation msgs is OFF',_msg) end if cfg.desUseAttackRadial then _msg = string.format ('%s\nRequire attack radial in designation is ON',_msg) else _msg = string.format ('%s\nRequire attack radial in designation is OFF',_msg) end UI.messageToUnit(unitName,_msg,{},10,false) elseif setting == 1 then -- Toggle display of Abort message cfg.abortMSG = not cfg.abortMSG if cfg.abortMSG then UI.messageToUnit(unitName,'Abort message is ON',false) else UI.messageToUnit(unitName,'Abort message is OFF',false) end elseif setting == 2 then -- Toggle display of Release data cfg.DisplayReleaseData = not cfg.DisplayReleaseData if cfg.DisplayReleaseData then UI.messageToUnit(unitName,'Release data display is ON',false) else UI.messageToUnit(unitName,'Release data display is OFF',false) end -- Distancce elseif setting == 3 then -- Distance meters cfg.DistanceUOM = 3 -- 1 = feet, 2 = yards, 3 = meters UI.messageToUnit(unitName,'Distance: Using meters',false) elseif setting == 4 then --Distance feet cfg.DistanceUOM = 1 -- 1 = feet, 2 = yards, 3 = meters UI.messageToUnit(unitName,'Distance: Using feet',false) elseif setting == 5 then -- Distance yards cfg.DistanceUOM = 2 --1 = feet, 2 = yards, 3 = meters UI.messageToUnit(unitName,'Distance: Using yards',false) -- Altitude elseif setting == 6 then -- Altitude meters cfg.AltitudeUOM = 3 -- 1 = feet, 3 = meters UI.messageToUnit(unitName,'Altitude: Using meters',false) elseif setting == 7 then -- Altitude feet cfg.AltitudeUOM = 1 -- 1 = feet, 3 = meters UI.messageToUnit(unitName,'Altitude: Using feet',false) -- Speed elseif setting == 8 then -- Speed kph cfg.SpeedUOM = 3 -- 1 = Knots, 2 = Mph, 3 = Kph UI.messageToUnit(unitName,'Speed: Using kph',false) elseif setting == 9 then -- Speed kt cfg.SpeedUOM= 1 -- 1 = Knots, 2 = Mph, 3 = Kph UI.messageToUnit(unitName,'Speed: Using knots',false) elseif setting == 10 then -- Speed mph cfg.SpeedUOM = 2 -- 1 = Knots, 2 = Mph, 3 = Kph UI.messageToUnit(unitName,'Speed: Using mph',false) -- Coordinate format --[[ elseif setting == 11 then -- Coordinate format MGRS cfg.CoordFormat = 1 -- 1 = MGRS, 2 = DMS, 3 = DM.mm UI.messageToUnit(unitName,'Coordinate format: Using MGRS',false) elseif setting == 12 then -- Coordinate format DMS cfg.CoordFormat = 2 -- 1 = MGRS, 2 = DMS, 3 = DM.mm UI.messageToUnit(unitName,'Coordinate format: Using DMS',false) elseif setting == 13 then -- Coordinate format DM.mm cfg.CoordFormat = 3 -- 1 = MGRS, 2 = DMS, 3 = DM.mm UI.messageToUnit(unitName,'Coordinate format: Using DM.mm',false) --]] elseif setting == 20 then -- Results reports cfg.resultsRPT = 0 UI.messageToUnit(unitName,'Results using Automatic selection',false) elseif setting == 21 then -- Results reports cfg.resultsRPT = 1 UI.messageToUnit(unitName,'Results using Impact results (individual) report',false) elseif setting == 22 then -- Results reports cfg.resultsRPT = 2 UI.messageToUnit(unitName,'Results using Impact results (grouped) report',false) elseif setting == 23 then -- Results reports cfg.resultsRPT = 3 UI.messageToUnit(unitName,'Results using BDA Pass Summary report',false) elseif setting == 30 then -- Toggle coords display in designation cfg.desDisplayCoords = not cfg.desDisplayCoords if cfg.desDisplayCoords then UI.messageToUnit(unitName,'Display coordinates in designation msgs is ON',false) else UI.messageToUnit(unitName,'Display coordinates in designation msgs is OFF',false) end elseif setting == 31 then -- Toggle coords display in designation cfg.desUseAttackRadial = not cfg.desUseAttackRadial if cfg.desUseAttackRadial then UI.messageToUnit(unitName,'Require attack radial in designation is ON',false) else UI.messageToUnit(unitName,'Require attack radial in designation is OFF',false) end elseif setting == 66 then -- respawn test for u,_ in pairs(TITS.TargetList) do TITS.respawnTarget(u) end -- for u,v in pairs(TITS.TargetList) do elseif setting == 70 then -- toggle illumination UI.illuminating = not UI.illuminating if UI.illuminating then trigger.action.outText( 'Starting illumination', 10 ) timer.scheduleFunction(UI.illuminate, {}, timer.getTime() + 1 ) else trigger.action.outText( 'Stoping illumination', 10 ) end end -- main if return true end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.messageToUnit(_unitName,_messageText,_soundTable,_displayTime,_displayToAll) --[[ local msg = {} msg.text = _messageText msg.msgFor = {units = {_unitName}} if TITS.messageBeep ~= '' then msg.sound = TITS.messageBeep end if _soundTable ~= nil then msg.multSound = _soundTable end if _displayTime == nil then _displayTime = 5 end msg.displayTime = _displayTime mist.message.add(msg) --]] -- bypass mist because of message history spamming if _displayToAll == nil then _displayToAll = true end if _displayTime == nil then _displayTime = 5 end local _unit = Unit.getByName(_unitName) local _groupId = _unit:getGroup():getID() local _callSign = _unit:getCallsign() if UI.messageBeep ~= '' then if _displayToAll then trigger.action.outSound(UI.messageBeep) else trigger.action.outSoundForGroup(_groupId,UI.messageBeep) end end local _msg = string.format('(%s): %s',_callSign,_messageText) if _displayToAll then trigger.action.outText( _msg, _displayTime) else trigger.action.outTextForGroup(_groupId, _msg, _displayTime) end return true end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.passStartEnd(_args) local unitName = _args[1] local value = _args[2] --local groupID = Unit.getByName(unitName):getGroup():getID() local unit = Unit.getByName(unitName) local groupID = unit:getGroup():getID() if unit:getPlayerName() then if value == 'in' then UI.messageToUnit(unitName,'Cleared in hot',{},10) -- reset prior attack results TITS.passInit(unitName) TITS.passStart(unitName) UI.toggleAttackMenu(unitName, "in") if pcall(function() return PTC.Version end ) then PTC.passTargetsUP = 0 PTC.passTargetsHIT = 0 end else -- value == 'off' local cfg = UI.unitConfig[unitName] local canEnd = TITS.passEnd(unitName) cfg.lastPassEnd = timer.getTime() if canEnd then local cfg = UI.unitConfig[unitName] UI.messageToUnit(unitName,'Standby for report',{},10) -- Show results if cfg.resultsRPT == 0 then -- automatic local passData = TITS.passGetData(unitName) local wf = passData.weaponsFired if UI.rowCount(wf) >= UI.MinWFgrpRPT then if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then UI.rptImpactResultsDesGrouped(unitName) else UI.rptImpactResultsGrouped(unitName) end else if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then UI.rptImpactResultsDes(unitName) else UI.rptImpactResults(unitName) end end elseif cfg.resultsRPT == 1 then UI.rptImpactResults(unitName) elseif cfg.resultsRPT == 2 then UI.rptImpactResultsGrouped(unitName) elseif cfg.resultsRPT == 3 then UI.rptBDAsummary(unitName) end -- if UI.DefaultResultReport UI.toggleAttackMenu(unitName, "off") else UI.messageToUnit(unitName,'Standby, weapons still in the air',{},10) end end end --if unit:getPlayerName() then return true end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.toggleAttackMenu(unitName, action) local unit = Unit.getByName(unitName) local groupID = unit:getGroup():getID() local cfg = UI.unitConfig[unitName] if action == 'in' then missionCommands.removeItemForGroup(groupID, cfg.rollInPath) cfg.offPath = missionCommands.addCommandForGroup(groupID,"Off - attack completed" , cfg.rootPath, UI.passStartEnd , {unitName,'off'}) else missionCommands.removeItemForGroup(groupID, cfg.offPath) cfg.rollInPath= missionCommands.addCommandForGroup(groupID,"Rolling in" , cfg.rootPath, UI.passStartEnd , {unitName,'in'}) end -- if action... return true end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.debugPassData(_unitName) local d = TITS.passGetData(_unitName) --local unit = Unit.getByName(_unitName) --local ammoTable = unit:getAmmo() --trigger.action.outText("ammoTable", 1) --trigger.action.outText(mist.utils.tableShow(ammoTable), 1) --trigger.action.outText("TargetList", 1) --trigger.action.outText(mist.utils.tableShow(TITS.TargetList), 1) --trigger.action.outText("targetHealth", 1) --trigger.action.outText(mist.utils.tableShow(d.targetHealth), 1) --trigger.action.outText("pass data", 1) --trigger.action.outText(mist.utils.tableShow(d), 1) trigger.action.outText("shellsFired", 1) trigger.action.outText(mist.utils.tableShow(d.shellsFired), 1) trigger.action.outText("shellHits", 1) trigger.action.outText(mist.utils.tableShow(d.shellHits), 1) trigger.action.outText("shellDisplayName", 1) trigger.action.outText(mist.utils.tableShow(d.shellDisplayName), 1) trigger.action.outText("getShellsFired", 1) local shellsFired = TITS.getShellsFired(_unitName) trigger.action.outText(mist.utils.tableShow(shellsFired), 1) --trigger.action.outText("weaponHits", 1) --trigger.action.outText(mist.utils.tableShow(d.weaponHits), 1) --trigger.action.outText("weaponsFired", 1) --trigger.action.outText(mist.utils.tableShow(d.weaponsFired), 1) --trigger.action.outText("impactData", 1) --trigger.action.outText(mist.utils.tableShow(d.impactData), 1) --trigger.action.outText("groups", 1) --trigger.action.outText(mist.utils.tableShow(d.groups), 1) return true end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rptWeaponsInFlight(_unitName) local passData = TITS.passGetData(_unitName) local weaponsFired = passData.weaponsFired local rpt = '' local t = timer.getTime() for k,v in pairs(weaponsFired) do if v.inFlight then et = t - v.timeStamp rpt = string.format('%s\n %s (%s s)',rpt, v.weaponType,mist.utils.round(et,0)) end end if rpt == '' then rpt = 'No weapons in flight' else rpt = string.format('Weapons in flight:%s',rpt) end UI.messageToUnit(_unitName,rpt,{},30) end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rptBDAsummary(_unitName) if TITS.onPass[_unitName] then UI.messageToUnit(_unitName,'Data not available yet, finish the pass',{},5) else local passData = TITS.passGetData(_unitName) local shellHits = passData.shellHits local weaponHits = passData.weaponHits local weaponsFired = passData.weaponsFired local targetHealth = passData.targetHealth local rptHitsData = {} local rptTargetsHit = {} local config = UI.unitConfig[_unitName] -- Get targets hit by shells and count of hits per shell type if #shellHits > 0 then for target,_ in pairs(shellHits) do if not rptTargetsHit[target] then rptTargetsHit[target] = true end for shellType,count in pairs(shellHits[target]) do if not rptHitsData[target] then rptHitsData[target] = {} end -- if not rptHitsData[target] then if not rptHitsData[target][shellType] then rptHitsData[target][shellType] = count else rptHitsData[target][shellType] = rptHitsData[target][shellType] + count end end -- for _,shellType in pairs(shellHits[target]) do end -- for target,_ in pairs(shellHits) do end -- if #shellHits > 0 then -- Get targets hit by weapons and count of hits by weapon type if UI.rowCount(weaponHits) > 0 then for weaponID, _ in pairs(weaponHits) do local weaponType = weaponsFired[weaponID].weaponType for target,_ in pairs (weaponHits[weaponID]) do if not rptTargetsHit[target] then rptTargetsHit[target] = true end if not rptHitsData[target] then rptHitsData[target] = {} end -- if not rptHitsData[target] then if not rptHitsData[target][weaponType] then rptHitsData[target][weaponType] = 1 else rptHitsData[target][weaponType] = rptHitsData[target][weaponType] + 1 end end -- for target,_ in pairs (weaponHits[weaponID] do end -- for weaponID, _ in pairs(weaponHits) do end -- if #weaponHits > 0 then ----- Show number of rows per table -- Process the data and generate report local rpt = '' if UI.rowCount(rptTargetsHit) > 0 then for target,_ in pairs(rptTargetsHit) do local section = '' local t_row = TITS.TargetList[target] local t_health = targetHealth[target] local hits = rptHitsData[target] local max_h = TITS.getMaxTargetLife(target) local sp_h = t_health.start_health local ep_h = t_health.end_health local pass_delta = (sp_h - ep_h)*100/max_h local overall_delta = ep_h*100/max_h local t_status = '' if overall_delta >= 0 and overall_delta < 10 then t_status = 'destroyed' elseif overall_delta >= 10 and overall_delta < 25 then t_status = 'heavily damaged' elseif overall_delta >= 25 and overall_delta < 50 then t_status = 'moderately damaged' elseif overall_delta >= 50 and overall_delta < 75 then t_status = 'slightly damaged' else t_status = 'operational' end -- if overall_delta >= 0 and overall_delta < 10 then section = string.format(" >>> %s is %s (pass damage: %s%%)",t_row.displayName,t_status,mist.utils.round(pass_delta,1)) -- hit details? rpt = string.format("%s\n%s",rpt,section) end -- for target,_ in pairs(rptHitsData) do end --if #rptHitsData > 0 then if rpt == '' then rpt = 'No targets hit in this pass' end if TITS.onPass[_unitName] then rpt = string.format('Pass BDA Summary (partial:)\n%s',rpt) else rpt = string.format('Pass BDA Summary:\n%s',rpt) end UI.messageToUnit(_unitName,rpt,{},30) end --if TITS.onPass[_unitName] then end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rptListCoord(_unitName) local _msg = '' local cfg = UI.unitConfig[_unitName] for t,v in pairs(TITS.TargetList) do if v.list_coor then local pos = TITS.getTargetPos(t) --local _coord, alt, auom = UI.getCoords(_unitName,pos) local _coord_mgrs,_coord_dms,_coord_dm, alt, auom = UI.getCoords(_unitName,pos) _msg = _msg..string.format('[%s]-> Elev: %i%s MSL\n %s\n %s\n %s\n',v.displayName,alt,auom,_coord_mgrs,_coord_dms,_coord_dm) end end -- if v.list_coor then if _msg ~= '' then _msg = string.format('Coordinates:\n%s',_msg) else _msg = 'Coordinates not available' end UI.messageToUnit(_unitName, _msg,{},30) return true end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rptImpactResults(_unitName) local config = UI.unitConfig[_unitName] local weaponsFired = TITS.getWeponsFired(_unitName) local shellsFired = TITS.getShellsFired(_unitName) local shellHits = TITS.getShellHits(_unitName) local title = '\nIndividual Impact Report (closest target)' if TITS.onPass[_unitName] then title = title..' (partial)' end if weaponsFired.count > 0 or shellsFired.total > 0 or shellHits.total > 0 then local rpt = '' local sec = '' -- -- Shell Hits ========================================================== -- if shellsFired.total > 0 or shellHits.total > 0 then local ROT = 0 if shellsFired.total > 0 then ROT = mist.utils.round(100*shellHits.total/shellsFired.total,1) sec = string.format(' %s Rounds fired / hits: %s%%\n',shellsFired.total,ROT) end --loop thru shells for _, d in pairs(shellHits.list) do local targetName = TITS.TargetList[d.target].displayName if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if d.target == UI.Designation.Designated then sec = string.format('%s -> %s hit %s rnds: %s - On Target!',sec,targetName, d.hits, d.shellType) else sec = string.format('%s -> %s hit %s rnds: %s - Wrong target!',sec,targetName, d.hits, d.shellType) end if config.desUseAttackRadial and UI.Designation.Designated == d.target then local delta = mist.utils.round((shellsFired.shootHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then sec = sec..' \n' else sec = string.format('%s \n', sec,delta) end end else sec = string.format('%s -> %s hit %s rnds: %s\n',sec,targetName, d.hits, d.shellType) end end -- for shellType, count in pairs(shellsFired.list) do end --if shellsFired.total > 0 then rpt = sec sec = '' -- -- Weapon Hits ========================================================== -- if weaponsFired.count > 0 then for _,wID in pairs(weaponsFired.list) do local weaponData = TITS.getWeaponData(_unitName, wID) local targetLN = string.format(" -> %s :",weaponData.weaponType) if weaponData.miss then local missDist,missDistUOM = UI.convertDistance(_unitName,TITS.MaxMissDistance) missDist = mist.utils.round(missDist,0) targetLN = string.format("%s no targets within %s%s from the impact",targetLN,missDist,missDistUOM) else local ct = weaponData.closestTarget local ctd = weaponData.impacts[ct] local ctDN= TITS.TargetList[ct].displayName local hitTXT = 'No effect' if ctd.hit then hitTXT ='Good effect' end if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if UI.Designation.Designated == ct then hitTXT =hitTXT..' on designated target!' else hitTXT =hitTXT..' on wrong target!' end if config.desUseAttackRadial and UI.Designation.Designated == ct then local delta = mist.utils.round((weaponData.releaseHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then hitTXT = hitTXT..' ' else hitTXT = string.format('%s ', hitTXT,delta) end end else hitTXT =hitTXT..' on target!' end targetLN = string.format("%s %s\n",targetLN,hitTXT) -- Impact data local dist,distUOM = UI.convertDistance(_unitName,ctd.distance) dist = mist.utils.round(dist,0) local direction = UI.getRelativeDirection(weaponData.releaseHeading ,ctd.targetPos, ctd.impactPos) direction = UI.getClockDirection(direction) local slantRange = mist.utils.get3DDist(weaponData.releasePosition, ctd.targetPos) local angDist,angDistUOM = UI.convertAngDistance(_unitName,ctd.distance/(slantRange * 0.1)) angDist = mist.utils.round(angDist,2) targetLN = string.format("%s %s %s from %s @ %s o'clock (%s %s)\n" , targetLN , dist , distUOM , ctDN , direction , angDist , angDistUOM ) --Release params if config.DisplayReleaseData then local pos = weaponData.releasePosition local height = land.getHeight({x = pos.x, y = pos.z}) local releaseAlt = pos.y - height -- in meters. Release AGL local alt, altUOM = UI.convertAltitude(_unitName,releaseAlt) alt = mist.utils.round(alt,0) local speed, speedUOM = UI.convertSpeed(_unitName,weaponData.releaseSpeed) speed = mist.utils.round(speed,0) local slant,slantUOM = UI.convertDistance(_unitName,slantRange) slant = mist.utils.round(slant,0) local pitch = mist.utils.round(weaponData.releasePitch,1) local roll = mist.utils.round(weaponData.releaseRoll,1) local yaw = mist.utils.round(weaponData.releaseYaw,1) local RIdistance,RIdistUOM = UI.convertDistance(_unitName,weaponData.rollInDistance) local RIat,RIatUOM = UI.convertAltitude(_unitName,weaponData.rollInAltitudeAT) RIdistance = mist.utils.round(RIdistance,0) RIat = mist.utils.round(RIat,0) local relP = string.format(" Rel. Params: Alt: %s%s AGL Speed: %s%s Hdg:%s°\n" , alt , altUOM , speed , speedUOM ,mist.utils.round(weaponData.releaseHeading,0) ) relP = string.format("%s S.RNG: %s%s P: %s R:%s Y:%s\n" , relP , slant , slantUOM , pitch , roll , yaw ) relP = string.format("%s Roll-in: Dist: %s%s Alt: %s%s (above target)\n",relP,RIdistance,RIdistUOM,RIat,RIatUOM) targetLN = string.format("%s%s",targetLN,relP) end -- if cfg.DisplayReleaseData then end -- if weaponData.miss then sec = sec..targetLN end -- for _,wID in pairs(weaponsFired.list) do end --if weaponsFired.count > 0 if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' -- -- Popups Hits ========================================================== -- if pcall(function() return PTC.Version end ) then if PTC.passTargetsUP > 0 then sec = string.format("%s Popups hit: %s/%s",sec,PTC.passTargetsHIT ,PTC.passTargetsUP ) end end if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' -- -- Show report ========================================================== -- rpt = string.format ('%s\n%s',title,rpt) UI.messageToUnit(_unitName,rpt,{},30) else -- if weaponsFired.count > 0 or shellsFired.total > 0 then UI.messageToUnit(_unitName,title..'\nThere is nothing to report.',{},5) end -- if weaponsFired.count > 0 or shellsFired.total > 0 then end --function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rptImpactResultsGrouped(_unitName) local config = UI.unitConfig[_unitName] local weaponsFired = TITS.getWeponsFired(_unitName) local shellsFired = TITS.getShellsFired(_unitName) local shellHits = TITS.getShellHits(_unitName) local title = '\nGrouped Impact Report (closest target)' if TITS.onPass[_unitName] then title = title..' (partial)' end if weaponsFired.count > 0 or shellsFired.total > 0 or shellHits.total > 0 then local rpt = '' local sec = '' -- -- Shell Hits ========================================================== -- if shellsFired.total > 0 or shellHits.total > 0 then local ROT = 0 if shellsFired.total > 0 then ROT = mist.utils.round(100*shellHits.total/shellsFired.total,1) sec = string.format(' %s Rounds fired / hits: %s%%\n',shellsFired.total,ROT) end --loop thru shells for _, d in pairs(shellHits.list) do local targetName = TITS.TargetList[d.target].displayName if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if d.target == UI.Designation.Designated then sec = string.format('%s -> %s hit %s rnds: %s - On Target!',sec,targetName, d.hits, d.shellType) else sec = string.format('%s -> %s hit %s rnds: %s - Wrong target!',sec,targetName, d.hits, d.shellType) end if config.desUseAttackRadial and UI.Designation.Designated == d.target then local delta = mist.utils.round((shellsFired.shootHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then sec = sec..' \n' else sec = string.format('%s \n', sec,delta) end end else sec = string.format('%s -> %s hit %s rnds: %s\n',sec,targetName, d.hits, d.shellType) end end -- for shellType, count in pairs(shellsFired.list) do end --if shellsFired.total > 0 then rpt = sec sec = '' -- -- Groups ========================================================== -- local groups = TITS.getGroups(_unitName) for _ , groupID in pairs(groups.list) do local groupData = TITS.getGroupData(_unitName, groupID) local targetLN = string.format(" -> %s :",groupData.weaponType) if groupData.miss then local missDist,missDistUOM = UI.convertDistance(_unitName,TITS.MaxMissDistance) missDist = mist.utils.round(missDist,0) targetLN = string.format("%s no targets within %s%s from the impact",targetLN,missDist,missDistUOM) else local ct = groupData.closestTarget local ctDN= TITS.TargetList[ct].displayName local hitTXT = 'No effect' if groupData.closestTargetHit then hitTXT ='Good effect' end if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if UI.Designation.Designated == ct then hitTXT =hitTXT..' on designated target!' else hitTXT =hitTXT..' on wrong target!' end if config.desUseAttackRadial and UI.Designation.Designated == ct then local delta = mist.utils.round((weaponData.releaseHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then hitTXT = hitTXT..' ' else hitTXT = string.format('%s ', hitTXT,delta) end end else hitTXT =hitTXT..' on target!' end targetLN = string.format("%s %s (%s/%s hits)\n",targetLN,hitTXT,groupData.closestTargetHitCount, groupData.weaponCount) -- Impact data local dist,distUOM = UI.convertDistance(_unitName,groupData.closestTargetDist) dist = mist.utils.round(dist,0) local direction = UI.getRelativeDirection(groupData.releaseHeading ,groupData.closestTargetPos, groupData.impactPos) direction = UI.getClockDirection(direction) local slantRange = mist.utils.get3DDist(groupData.releasePosition, groupData.closestTargetPos) local angDist,angDistUOM = UI.convertAngDistance(_unitName,groupData.closestTargetDist/(slantRange * 0.1)) angDist = mist.utils.round(angDist,2) local grpSize,grpSizeUOM = UI.convertAngDistance(_unitName,groupData.size) grpSize = mist.utils.round(grpSize,2) -- hit count / weapon count targetLN = string.format("%s %s %s from %s @ %s o'clock (%s %s)\n" , targetLN , dist , distUOM , ctDN , direction , angDist , angDistUOM ) targetLN = string.format("%s Group size: %s%s\n" , targetLN , grpSize , grpSizeUOM ) --Release params if config.DisplayReleaseData then local pos = groupData.releasePosition local height = land.getHeight({x = pos.x, y = pos.z}) local releaseAlt = pos.y - height -- in meters. Release AGL local alt, altUOM = UI.convertAltitude(_unitName,releaseAlt) alt = mist.utils.round(alt,0) local speed, speedUOM = UI.convertSpeed(_unitName,groupData.releaseSpeed) speed = mist.utils.round(speed,0) local slant,slantUOM = UI.convertDistance(_unitName,slantRange) slant = mist.utils.round(slant,0) local pitch = mist.utils.round(groupData.releasePitch,1) local roll = mist.utils.round(groupData.releaseRoll,1) local yaw = mist.utils.round(groupData.releaseYaw,1) local RIdistance,RIdistUOM = UI.convertDistance(_unitName,groupData.rollInDistance) local RIat,RIatUOM = UI.convertAltitude(_unitName,groupData.rollInAltitudeAT) RIdistance = mist.utils.round(RIdistance,0) RIat = mist.utils.round(RIat,0) local relP = string.format(" Rel. Params: Alt: %s%s AGL Speed: %s%s Hdg:%s°\n" , alt , altUOM , speed , speedUOM ,mist.utils.round(groupData.releaseHeading,0) ) relP = string.format("%s S.RNG: %s%s P: %s R:%s Y:%s\n" , relP , slant , slantUOM , pitch , roll , yaw ) relP = string.format("%s Roll-in: Dist: %s%s Alt: %s%s (above target)\n",relP,RIdistance,RIdistUOM,RIat,RIatUOM) targetLN = string.format("%s%s",targetLN,relP) end -- if cfg.DisplayReleaseData then end -- if weaponData.miss then sec = sec..targetLN end -- for _,groupID in pairs(groups) do -- -- Popups Hits ========================================================== -- if pcall(function() return PTC.Version end ) then if PTC.passTargetsUP > 0 then sec = string.format("%s Popups hit: %s/%s",sec,PTC.passTargetsHIT ,PTC.passTargetsUP ) end end if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' -- -- Show report ========================================================== -- rpt = string.format ('%s\n%s',title,rpt) UI.messageToUnit(_unitName,rpt,{},30) else -- if weaponsFired.count > 0 or shellsFired.total > 0 then UI.messageToUnit(_unitName,title..'\nThere is nothing to report.',{},5) end -- if weaponsFired.count > 0 or shellsFired.total > 0 then end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rptImpactResultsDes(_unitName) local config = UI.unitConfig[_unitName] local weaponsFired = TITS.getWeponsFired(_unitName) local shellsFired = TITS.getShellsFired(_unitName) local shellHits = TITS.getShellHits(_unitName) local title = '\nIndividual Impact Report (designated target)' if TITS.onPass[_unitName] then title = title..' (partial)' end if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if weaponsFired.count > 0 or shellsFired.total > 0 or shellHits.total > 0 then local rpt = '' local sec = '' -- Header local ct = UI.Designation.Designated local ctDN= TITS.TargetList[ct].displayName rpt = string.format(' Target: %s',ctDN) if config.desUseAttackRadial then rpt = string.format('%s / Attack radial: %s°',rpt,UI.Designation.AttackRadial) end -- -- Shell Hits ========================================================== -- if shellsFired.total > 0 or shellHits.total > 0 then local ROT = 0 if shellsFired.total > 0 then ROT = mist.utils.round(100*shellHits.total/shellsFired.total,1) sec = string.format(' %s Rounds fired / hits: %s%%\n',shellsFired.total,ROT) end --loop thru shells for _, d in pairs(shellHits.list) do local targetName = TITS.TargetList[d.target].displayName if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if d.target == UI.Designation.Designated then sec = string.format('%s -> %s hit %s rnds: %s - On Target!',sec,targetName, d.hits, d.shellType) else sec = string.format('%s -> %s hit %s rnds: %s - Wrong target!',sec,targetName, d.hits, d.shellType) end if config.desUseAttackRadial and UI.Designation.Designated == d.target then local delta = mist.utils.round((shellsFired.shootHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then sec = sec..' \n' else sec = string.format('%s \n', sec,delta) end end else sec = string.format('%s -> %s hit %s rnds: %s\n',sec,targetName, d.hits, d.shellType) end end -- for shellType, count in pairs(shellsFired.list) do end --if shellsFired.total > 0 then if sec ~= '' then if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' end -- -- Weapon Hits ========================================================== -- if weaponsFired.count > 0 then for _,wID in pairs(weaponsFired.list) do local weaponData = TITS.getWeaponData(_unitName, wID) local targetLN = string.format(" -> %s :",weaponData.weaponType) local missDist,missDistUOM = UI.convertDistance(_unitName,TITS.MaxMissDistance) if weaponData.miss then missDist = mist.utils.round(missDist,0) targetLN = string.format("%s no targets within %s%s from the impact",targetLN,missDist,missDistUOM) else local ctd = weaponData.impacts[ct] local hitTXT = 'No effect' if ctd then if ctd.hit then hitTXT ='Good effect' end if config.desUseAttackRadial and UI.Designation.Designated == ct then local delta = mist.utils.round((weaponData.releaseHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then hitTXT = hitTXT..' ' else hitTXT = string.format('%s ', hitTXT,delta) end end targetLN = string.format("%s %s\n",targetLN,hitTXT) -- Impact data local dist,distUOM = UI.convertDistance(_unitName,ctd.distance) dist = mist.utils.round(dist,0) local direction = UI.getRelativeDirection(weaponData.releaseHeading ,ctd.targetPos, ctd.impactPos) direction = UI.getClockDirection(direction) local slantRange = mist.utils.get3DDist(weaponData.releasePosition, ctd.targetPos) local angDist,angDistUOM = UI.convertAngDistance(_unitName,ctd.distance/(slantRange * 0.1)) angDist = mist.utils.round(angDist,2) targetLN = string.format("%s %s %s from target @ %s o'clock (%s %s)\n" , targetLN , dist , distUOM , direction , angDist , angDistUOM ) --Release params if config.DisplayReleaseData then local pos = weaponData.releasePosition local height = land.getHeight({x = pos.x, y = pos.z}) local releaseAlt = pos.y - height -- in meters. Release AGL local alt, altUOM = UI.convertAltitude(_unitName,releaseAlt) alt = mist.utils.round(alt,0) local speed, speedUOM = UI.convertSpeed(_unitName,weaponData.releaseSpeed) speed = mist.utils.round(speed,0) local slant,slantUOM = UI.convertDistance(_unitName,slantRange) slant = mist.utils.round(slant,0) local pitch = mist.utils.round(weaponData.releasePitch,1) local roll = mist.utils.round(weaponData.releaseRoll,1) local yaw = mist.utils.round(weaponData.releaseYaw,1) local relP = string.format(" Rel. Params: Alt: %s%s AGL Speed: %s%s Hdg:%s°\n" , alt , altUOM , speed , speedUOM ,mist.utils.round(weaponData.releaseHeading,0) ) relP = string.format("%s S.RNG: %s%s P: %s R:%s Y:%s\n" , relP , slant , slantUOM , pitch , roll , yaw ) targetLN = string.format("%s%s",targetLN,relP) end -- if cfg.DisplayReleaseData then else -- if ctd then targetLN = string.format("%s no impact within %s%s of the designated target",targetLN,missDist,missDistUOM) end -- if ctd then end -- if weaponData.miss then sec = sec..targetLN end -- for _,wID in pairs(weaponsFired.list) do end --if weaponsFired.count > 0 if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' -- -- Popups Hits ========================================================== -- if pcall(function() return PTC.Version end ) then if PTC.passTargetsUP > 0 then sec = string.format("%s Popups hit: %s/%s",sec,PTC.passTargetsHIT ,PTC.passTargetsUP ) end end if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' -- -- Show report ========================================================== -- rpt = string.format ('%s\n%s',title,rpt) UI.messageToUnit(_unitName,rpt,{},30) else -- if weaponsFired.count > 0 or shellsFired.total > 0 then UI.messageToUnit(_unitName,title..'\nThere is nothing to report.',{},5) end -- if weaponsFired.count > 0 or shellsFired.total > 0 then else UI.messageToUnit(_unitName,title..'\nThere is no designated target.',{},5) end --if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then end --function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rptImpactResultsDesGrouped(_unitName) local config = UI.unitConfig[_unitName] local weaponsFired = TITS.getWeponsFired(_unitName) local shellsFired = TITS.getShellsFired(_unitName) local shellHits = TITS.getShellHits(_unitName) local title = '\nGrouped Impact Report (designated target)' if TITS.onPass[_unitName] then title = title..' (partial)' end if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if weaponsFired.count > 0 or shellsFired.total > 0 or shellHits.total > 0 then local rpt = '' local sec = '' -- Header local ct = UI.Designation.Designated local ctDN= TITS.TargetList[ct].displayName rpt = string.format(' Target: %s',ctDN) if config.desUseAttackRadial then rpt = string.format('%s / Attack radial: %s°',rpt,UI.Designation.AttackRadial) end -- -- Shell Hits ========================================================== -- if shellsFired.total > 0 or shellHits.total > 0 then local ROT = 0 if shellsFired.total > 0 then ROT = mist.utils.round(100*shellHits.total/shellsFired.total,1) sec = string.format(' %s Rounds fired / hits: %s%%\n',shellsFired.total,ROT) end --loop thru shells for _, d in pairs(shellHits.list) do local targetName = TITS.TargetList[d.target].displayName if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then if d.target == UI.Designation.Designated then sec = string.format('%s -> %s hit %s rnds: %s - On Target!',sec,targetName, d.hits, d.shellType) else sec = string.format('%s -> %s hit %s rnds: %s - Wrong target!',sec,targetName, d.hits, d.shellType) end if config.desUseAttackRadial and UI.Designation.Designated == d.target then local delta = mist.utils.round((shellsFired.shootHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then sec = sec..' \n' else sec = string.format('%s \n', sec,delta) end end else sec = string.format('%s -> %s hit %s rnds: %s\n',sec,targetName, d.hits, d.shellType) end end -- for shellType, count in pairs(shellsFired.list) do end --if shellsFired.total > 0 then if sec ~= '' then if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' end -- -- Groups ========================================================== -- if weaponsFired.count > 0 then local groups = TITS.getGroups(_unitName) for _,groupID in pairs(groups.list) do local groupData = TITS.getGroupData(_unitName, groupID) local targetLN = string.format(" -> %s :",groupData.weaponType) local missDist,missDistUOM = UI.convertDistance(_unitName,TITS.MaxMissDistance) if groupData.miss then missDist = mist.utils.round(missDist,0) targetLN = string.format("%s no targets within %s%s from the impact",targetLN,missDist,missDistUOM) else -- get all the weapons in the group with an impact on the target local ctd = {} local ctPos = {} local ctHits = 0 for _,row in pairs(groupData.TargetImpacts) do if row.target == UI.Designation.Designated then table.insert(ctd,row.targetPos) ctPos =row.targetPos if row.hit then ctHits = ctHits + 1 end end end --local ctd = weaponData.impacts[ct] local hitTXT = 'No effect' if UI.rowCount(ctd) > 0 then if UI.inTable(UI.Designation.Designated,groupData.targetsHit) then hitTXT ='Good effect' end if config.desUseAttackRadial and UI.Designation.Designated == ct then local delta = mist.utils.round((groupData.releaseHeading - UI.MagneticVar) - UI.Designation.AttackRadial, 0) if delta >= 180 then delta = 360-delta end if delta == 0 then hitTXT = hitTXT..' ' else hitTXT = string.format('%s ', hitTXT,delta) end end targetLN = string.format("%s %s (%s/%s hits)\n",targetLN,hitTXT,ctHits, groupData.weaponCount) --targetLN = string.format("%s %s\n",targetLN,hitTXT) -- Impact data from group center local distance = mist.utils.get3DDist(groupData.impactPos,ctPos) local dist,distUOM = UI.convertDistance(_unitName,distance) dist = mist.utils.round(dist,0) local direction = UI.getRelativeDirection(groupData.releaseHeading ,ctPos, groupData.impactPos) direction = UI.getClockDirection(direction) local slantRange = mist.utils.get3DDist(groupData.releasePosition, ctPos) local angDist,angDistUOM = UI.convertAngDistance(_unitName,distance/(slantRange * 0.1)) angDist = mist.utils.round(angDist,2) local grpSize,grpSizeUOM = UI.convertAngDistance(_unitName,groupData.size) grpSize = mist.utils.round(grpSize,2) targetLN = string.format("%s %s %s from target @ %s o'clock (%s %s)\n" , targetLN , dist , distUOM , direction , angDist , angDistUOM ) targetLN = string.format("%s Group size: %s%s\n" , targetLN , grpSize , grpSizeUOM ) --Release params if config.DisplayReleaseData then local pos = groupData.releasePosition local height = land.getHeight({x = pos.x, y = pos.z}) local releaseAlt = pos.y - height -- in meters. Release AGL local alt, altUOM = UI.convertAltitude(_unitName,releaseAlt) alt = mist.utils.round(alt,0) local speed, speedUOM = UI.convertSpeed(_unitName,groupData.releaseSpeed) speed = mist.utils.round(speed,0) local slant,slantUOM = UI.convertDistance(_unitName,slantRange) slant = mist.utils.round(slant,0) local pitch = mist.utils.round(groupData.releasePitch,1) local roll = mist.utils.round(groupData.releaseRoll,1) local yaw = mist.utils.round(groupData.releaseYaw,1) local relP = string.format(" Rel. Params: Alt: %s%s AGL Speed: %s%s Hdg:%s°\n" , alt , altUOM , speed , speedUOM , mist.utils.round(groupData.releaseHeading,0) ) relP = string.format("%s S.RNG: %s%s P: %s R:%s Y:%s\n" , relP , slant , slantUOM , pitch , roll , yaw ) targetLN = string.format("%s%s",targetLN,relP) end -- if cfg.DisplayReleaseData then else -- if ctd then targetLN = string.format("%s no impact within %s%s of the designated target",targetLN,missDist,missDistUOM) end -- if ctd then end -- if weaponData.miss then sec = sec..targetLN end -- for _,wID in pairs(weaponsFired.list) do end --if weaponsFired.count > 0 if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' -- -- Popups Hits ========================================================== -- if pcall(function() return PTC.Version end ) then if PTC.passTargetsUP > 0 then sec = string.format("%s Popups hit: %s/%s",sec,PTC.passTargetsHIT ,PTC.passTargetsUP ) end end if rpt ~= '' then rpt = string.format ('%s\n%s',rpt,sec) else rpt = sec end sec = '' -- -- Show report ========================================================== -- rpt = string.format ('%s\n%s',title,rpt) UI.messageToUnit(_unitName,rpt,{},30) else -- if weaponsFired.count > 0 or shellsFired.total > 0 then UI.messageToUnit(_unitName,title..'\nThere is nothing to report.',{},5) end -- if weaponsFired.count > 0 or shellsFired.total > 0 then else UI.messageToUnit(_unitName,title..'\nThere is no designated target.',{},5) end --if UI.Designation.type ~=0 and UI.Designation.Designated ~= '' then end --function -- --################################################################################################################################################# -- ############# Illumination ############### --################################################################################################################################################# -- function UI.AddIlluminationZone(_zoneName, _Rounds) UI.illuminationZones[_zoneName] = _Rounds end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.illuminate(params, time) if UI.illuminating then if UI.rowCount(UI.illuminationZones) > 0 then for z,r in pairs(UI.illuminationZones) do for i=1,r do local pos = mist.utils.makeVec3(mist.getRandomPointInZone(z),mist.random(450,550)) trigger.action.illuminationBomb(pos,7500) end end end return timer.getTime() + 150 else return nil end end -- function -------------------- -- --################################################################################################################################################# -- ############# Designation ############### --################################################################################################################################################# -- function UI.AddDesignationZone(_ID, _Name) UI.DesignationZones[_ID] = { id = _ID, name = _Name} end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.AddDesignator(_name, _displayName, _zones, _allowLaser, _allowIR, _code) local dn = _displayName if _displayName == '' then dn = _name end UI.Designators[_name] = { name = _name , displayName = dn , zones = _zones , allowLaser = _allowLaser , allowIR = _allowIR , code = _code } end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.desSetZone(_args) local unitName = _args[1] local zoneID = _args[2] UI.Designation.zone = zoneID local z = UI.DesignationZones[zoneID] UI.messageToUnit(unitName,string.format('Designation zone set to: %s',z.name)) end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.desWP(_args) local _unitName = _args[1] local isNew = _args[2] local ERROR = 0 local targetList = {} local n = 0 -- Number of designatable targets in zone local tgt -- selectec target local cfg = UI.unitConfig[_unitName] -- Get WP designatable targets in the selected zone if isNew then for _,row in pairs(TITS.TargetList) do if row.des_zone == UI.Designation.zone and row.des_wp and TITS.getTargetLife(row.name) > 0 then n = n + 1 targetList[n] = row.name end end -- for t,row in pairs(TITS.TargetList) do if n == 0 then ERROR = 1 -- No designatable targets in zone else local r = mist.random(1,n) tgt = targetList[r] end else -- if isNew or UI.Designation.Designated == '' then tgt = UI.Designation.Designated if TITS.getTargetLife(tgt) <= 0 then ERROR = 2 -- prior designated target is dead end end -- if isNew then if ERROR == 0 then -- Designate the target --select a random target local tgtPos = TITS.getTargetPos(tgt) local markPos = mist.utils.makeVec3GL(mist.getRandPointInCircle(tgtPos,UI.maxWPmarkDist,UI.minWPmarkDist,0,360)) local _dir = mist.utils.toDegree(mist.utils.getDir(mist.vec.sub(tgtPos, markPos))) local _dist = mist.utils.get2DDist(markPos,tgtPos) local _direction = '' local oTarget = TITS.TargetList[tgt] if _dir >= 0 and _dir < 22 then _direction = 'North' elseif _dir >= 22 and _dir < 68 then _direction = 'North East' elseif _dir >= 68 and _dir < 113 then _direction = 'East' elseif _dir >= 113 and _dir < 158 then _direction = 'South East' elseif _dir >= 158 and _dir < 203 then _direction = 'South' elseif _dir >= 203 and _dir < 248 then _direction = 'South West' elseif _dir >= 248 and _dir < 293 then _direction = 'West' elseif _dir >= 293 and _dir < 338 then _direction = 'North West' elseif _dir >= 338 and _dir <= 360 then _direction = 'North' end local ar_brief = '' if cfg.desUseAttackRadial then UI.Designation.AttackRadial = mist.random(1,359) ar_brief = string.format('\nAttack radial %s° (magnetic)',UI.Designation.AttackRadial) end -- prepare the briefing local dist,distUOM = UI.convertDistance(_unitName,_dist) dist = mist.utils.round(dist,0) if distUOM == 'ft' then dist = math.floor(dist / 25) * 25 else dist = math.floor(dist / 10) * 10 end local briefing = string.format("%s marked with WP\n%i %s, %s from the mark%s",oTarget.displayName, dist,distUOM, _direction,ar_brief) UI.Designation.Designator = '' -- unit name UI.Designation.Designated = tgt UI.Designation.type = 1 -- 0 = none, 1 = WP , 2 = laser, 3 = IR UI.Designation.briefing = briefing trigger.action.smoke(markPos, trigger.smokeColor.White ) UI.messageToUnit(_unitName,briefing,{},45) else local errorMSG ='' if ERROR == 1 then errorMSG = string.format('There are no designatable targets in zone %s',UI.DesignationZones[UI.Designation.zone].name ) elseif ERROR == 2 then errorMSG ='Target is dead. Select a new one' end UI.messageToUnit(_unitName,errorMSG,{},45) UI.Designation.Designator = '' -- unit name UI.Designation.Designated = '' UI.Designation.type = 0 -- 0 = none, 1 = WP , 2 = laser, 3 = IR UI.Designation.briefing = '' end --if not ERROR then end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.desNM(_unitName) local config = UI.unitConfig[_unitName] local ERROR = 0 local targetList = {} local n = 0 -- Number of designatable targets in zone local tgt -- selectec target -- Get WP designatable targets in the selected zone for _,row in pairs(TITS.TargetList) do if row.des_zone == UI.Designation.zone and row.des_nomark and TITS.getTargetLife(row.name) > 0 then n = n + 1 targetList[n] = row.name end end -- for t,row in pairs(TITS.TargetList) do if n == 0 then ERROR = 1 -- No designatable targets in zone else local r = mist.random(1,n) tgt = targetList[r] end if ERROR == 0 then -- Designate the target -- prepare the briefing local oTarget = TITS.TargetList[tgt] local cfg = UI.unitConfig[_unitName] local ar_brief = '' if cfg.desUseAttackRadial then UI.Designation.AttackRadial = mist.random(1,359) ar_brief = string.format('\nAttack radial %s° (magnetic)',UI.Designation.AttackRadial) end local briefing = '' if config.desDisplayCoords then local pos = TITS.getTargetPos(tgt) --local _coord, alt, auom = UI.getCoords(_unitName,pos) local _coord_mgrs,_coord_dms,_coord_dm, alt, auom = UI.getCoords(_unitName,pos) --Elev: %i%s MSL\n %s\n %s\n %s\n' briefing = string.format("\nYour target is %s at Elev: %i%s MSL\n %s\n %s\n %s\n",oTarget.displayName,alt,auom,_coord_mgrs,_coord_dms,_coord_dm) else briefing = string.format("\nYour target is %s",oTarget.displayName) end briefing = string.format("%s%s",briefing,ar_brief) if oTarget.uiText1 ~= '' then briefing = string.format('%s\n%s',briefing,oTarget.uiText1) end if oTarget.uiText2 ~= '' then briefing = string.format('%s\n%s',briefing,oTarget.uiText2) end if oTarget.uiText3 ~= '' then briefing = string.format('%s\n%s',briefing,oTarget.uiText3) end UI.Designation.Designator = '' -- unit name UI.Designation.Designated = tgt UI.Designation.type = 4 -- 0 = none, 1 = WP , 2 = laser, 3 = IR, 4 = No mark UI.Designation.briefing = briefing UI.messageToUnit(_unitName,briefing,{},45) else local errorMSG ='' if ERROR == 1 then errorMSG = string.format('There are no designatable targets in zone %s',UI.DesignationZones[UI.Designation.zone].name ) end UI.messageToUnit(_unitName,errorMSG,{},45) UI.Designation.Designator = '' -- unit name UI.Designation.Designated = '' UI.Designation.type = 0 -- 0 = none, 1 = WP , 2 = laser, 3 = IR UI.Designation.briefing = '' end --if not ERROR then end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.desRPTBrief(_unitName) if UI.Designation.type ~= 0 then UI.messageToUnit(_unitName,UI.Designation.briefing,{},45) else UI.messageToUnit(_unitName,'No targets designated',{},45) end end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.desCancel(_unitName) UI.Designation.Designator = '' -- unit name UI.Designation.Designated = '' UI.Designation.type = 0 -- 0 = none, 1 = WP , 2 = laser, 3 = IR, 4 = No mark, 23 = laser/IR UI.Designation.briefing = '' UI.Designation.spotON = false UI.messageToUnit(_unitName,'Designation cancelled',{},5) end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.desLaserIR(_args) local _unitName = _args[1] local desType = _args[2] -- laser | IR local designators = {} local targets = {} local Nt = 0 local Nd = 0 local cfg = UI.unitConfig[_unitName] if UI.Designation.type == 2 or UI.Designation.type == 3 or UI.Designation.type == 23 then UI.messageToUnit(_unitName,'There is a prior laser/IR designation. Cancel it first',{},15) return false end -- get designatable targets is zone for _,row in pairs(TITS.TargetList) do if row.des_zone == UI.Designation.zone and TITS.getTargetLife(row.name) > 0 and ((row.des_laser and desType == 'laser') or (row.des_ir and desType == 'IR') or (desType == 'laser/IR' and row.des_laser and row.des_ir ) ) then Nt = Nt + 1 targets[Nt] = row.name end end -- for t,row in pairs(TITS.TargetList) do -- get designators in zone for _,row in pairs(UI.Designators) do if UI.inTable(UI.Designation.zone, row.zones ) and ((row.allowLaser and desType == 'laser') or (row.allowIR and desType == 'IR') or (desType == 'laser/IR' and row.allowLaser and row.allowIR ) ) then -- check designator life local u = Unit.getByName(row.name) if u then Nd = Nd + 1 designators[Nd] = row.name end end end -- for t,row in pairs(TITS.TargetList) do if Nt > 0 and Nd > 0 then local Rt = mist.random(1,Nt) local Rd = mist.random(1,Nd) UI.Designation.Designator = designators[Rd] UI.Designation.Designated = targets[Rt] local designator = Unit.getByName(UI.Designation.Designator ) local desPos = TITS.getTargetPos(UI.Designation.Designated) desPos = { x = desPos.x, y = desPos.y + 2.0, z = desPos.z } if desType == 'laser' then UI.Designation.type = 2 UI.Designation.ray1 = Spot.createLaser(designator, {x = 0, y = 1, z = 0}, desPos , UI.Designators[UI.Designation.Designator].code) elseif desType == 'IR' then UI.Designation.type = 3 UI.Designation.ray1 = Spot.createInfraRed(designator, {x = 0, y = 1, z = 0}, desPos) else UI.Designation.type = 23 UI.Designation.ray1 = Spot.createLaser(designator, {x = 0, y = 1, z = 0}, desPos, UI.Designators[UI.Designation.Designator].code) UI.Designation.ray2 = Spot.createInfraRed(designator, {x = 0, y = 1, z = 0}, desPos) end local function updateRay() if UI.Designation.type == 2 or UI.Designation.type == 3 then local pos = TITS.getTargetPos(UI.Designation.Designated) pos = { x = pos.x, y = pos.y + 2.0, z = pos.z } UI.Designation.ray1:setPoint(pos) timer.scheduleFunction(updateRay, {}, timer.getTime() + 0.5) elseif UI.Designation.type == 23 then local pos = TITS.getTargetPos(UI.Designation.Designated) pos = { x = pos.x, y = pos.y + 2.0, z = pos.z } UI.Designation.ray1:setPoint(pos) UI.Designation.ray2:setPoint(pos) timer.scheduleFunction(updateRay, {}, timer.getTime() + 0.5) else UI.Designation.ray1:destroy() if UI.Designation.ray2 then UI.Designation.ray2:destroy() end end end timer.scheduleFunction(updateRay, {}, timer.getTime() + 0.5) -- prepare briefing local ar_brief = '' if cfg.desUseAttackRadial then UI.Designation.AttackRadial = mist.random(1,359) ar_brief = string.format('\nAttack radial %s° (magnetic)',UI.Designation.AttackRadial) end local dDN = UI.Designators[UI.Designation.Designator].displayName local t = TITS.TargetList[UI.Designation.Designated] local brief = string.format('%s designated by %s from %s', t.displayName, desType, dDN) if UI.Designation.type == 2 or UI.Designation.type == 23 then brief = string.format('%s with code: %s',brief,UI.Designators[UI.Designation.Designator].code) end if cfg.desDisplayCoords then local pos = TITS.getTargetPos(UI.Designation.Designated) --local _coord, alt, auom = UI.getCoords(_unitName,pos) local _coord_mgrs,_coord_dms,_coord_dm, alt, auom = UI.getCoords(_unitName,pos) brief = string.format('%s\ntarget coords: Elev: %i%s MSL\n %s\n %s\n %s',brief,alt,auom, _coord_mgrs,_coord_dms,_coord_dm) end brief = string.format('%s%s',brief,ar_brief) local oTarget = TITS.TargetList[UI.Designation.Designated] if oTarget.uiText1 ~= '' then brief = string.format('%s\n%s',brief,oTarget.uiText1) end if oTarget.uiText2 ~= '' then brief = string.format('%s\n%s',brief,oTarget.uiText2) end if oTarget.uiText3 ~= '' then brief = string.format('%s\n%s',brief,oTarget.uiText3) end UI.messageToUnit(_unitName,brief,{},45) else -- if Nt > 0 and Nd > 0 then local errorMSG = string.format('There are no designatable targets or designators in zone %s',UI.DesignationZones[UI.Designation.zone].name ) UI.messageToUnit(_unitName,errorMSG,{},45) end return true end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- -- --################################################################################################################################################# -- ############# Utility ############### --################################################################################################################################################# -- function UI.getCoords(_unitName,pos) local lat, lon, alt = coord.LOtoLL(pos) --MGRS local _coord_mgrs = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), 5):gsub(string.char(9),' ') -- DMS local _coord_dms = mist.tostringLL(lat, lon, 3, true):gsub(string.char(9),' ') -- DM.mm local _coord_dm = mist.tostringLL(lat, lon, 4):gsub(string.char(9),' ') local a,auom = UI.convertAltitude(_unitName,alt) return _coord_mgrs,_coord_dms,_coord_dm, a, auom --[[ local cfg = UI.unitConfig[_unitName] local _coord = '' local lat, lon, alt = coord.LOtoLL(pos) if cfg.CoordFormat == 1 then --MGRS _coord = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), 5) elseif cfg.CoordFormat == 2 then -- DMS _coord = mist.tostringLL(lat, lon, 2, true) else -- DM.mm _coord = mist.tostringLL(lat, lon, 2) end _coord = _coord:gsub(string.char(9),' ') local a,auom = UI.convertAltitude(_unitName,alt) return _coord, a, auom --]] end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.convertSpeed(_unitName,_speed) local s local u local cfg = UI.unitConfig[_unitName] -- 1 = Knots, 2 = Mph, 3 = Kph -- _speed in m/s if cfg.SpeedUOM == 1 then s = _speed * 1.943844 u = 'kt' elseif cfg.SpeedUOM == 2 then s = _speed * 2.23694 u = 'mph' else s = _speed * 36 u = 'kph' end return s,u end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.convertDistance(_unitName,_distance) local s local u local cfg = UI.unitConfig[_unitName] -- 1 = feet, 2 = yards, 3 = meters -- _distance in meters if cfg.DistanceUOM == 1 then s = _distance * 3.28084 u = 'ft' elseif cfg.DistanceUOM == 2 then s = _distance * 1.093613 u = 'yd' else s = _distance u = 'm' end return s,u end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.convertAngDistance(_unitName,_distance) local s local u local cfg = UI.unitConfig[_unitName] -- 1 = feet, 2 = yards, 3 = meters -- _distance in meters if cfg.DistanceUOM == 1 or cfg.DistanceUOM == 2 then s = _distance * 3.4377492368197 u = 'MOA' else s = _distance u = 'mils' end return s,u end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.convertAltitude(_unitName,_altitude) local s local u local cfg = UI.unitConfig[_unitName] -- 1 = feet, 2 = yards, 3 = meters -- _distance in meters if cfg.AltitudeUOM == 1 then s = _altitude * 3.28084 u = 'ft' else s = _altitude u = 'm' end return s,u end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.getRelativeDirection(_refHeading,_point1, _point2) local tgtBearing local xUnit = _point1.x local yUnit = _point1.z local xZone = _point2.x local yZone = _point2.z local xDiff = xUnit - xZone local yDiff = yUnit - yZone local tgtAngle = math.deg(math.atan(yDiff/xDiff)) if xDiff > 0 then tgtBearing = 180 + tgtAngle end if xDiff < 0 and tgtAngle > 0 then tgtBearing = tgtAngle end if xDiff < 0 and tgtAngle < 0 then tgtBearing = 360 + tgtAngle end tgtBearing = tgtBearing - _refHeading if tgtBearing > 360 then tgtBearing = tgtBearing - 360 end if tgtBearing < 0 then tgtBearing = 360 + tgtBearing end return tgtBearing end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.getClockDirection(_direction) -- by cfrag if not _direction then return 0 end while _direction < 0 do _direction = _direction + 360 end if _direction < 15 then -- special case 12 o'clock past 12 o'clock return 12 end _direction = _direction + 15 -- add offset so we get all other times correct return math.floor(_direction/30) end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.getDirection(_point1, _point2) local tgtBearing local xUnit = _point1.x local yUnit = _point1.z local xZone = _point2.x local yZone = _point2.z local xDiff = xUnit - xZone local yDiff = yUnit - yZone local tgtAngle = math.deg(math.atan(yDiff/xDiff)) if xDiff > 0 then tgtBearing = 180 + tgtAngle end if xDiff < 0 and tgtAngle > 0 then tgtBearing = tgtAngle end if xDiff < 0 and tgtAngle < 0 then tgtBearing = 360 + tgtAngle end return tgtBearing end ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.rowCount(t) local r = 0 if t then for _,_ in pairs(t) do r = r + 1 end end return r end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- function UI.inTable(v,t) local r = true for _,j in pairs(t) do if j == v then return true end end return false end -- function ----------------------------------------------------------------------------------------------------------------------------------------------------- -- --################################################################################################################################################# -- ############# Main body ############### --################################################################################################################################################# -- do -- load event handler world.addEventHandler(UI.EventHandler) trigger.action.outText( 'Range UI - v'..UI.Version, 1 ) end