1310 lines
47 KiB
Lua
1310 lines
47 KiB
Lua
TITS = {}
|
|
TITS.Version = '2.3.01'
|
|
|
|
|
|
--###################################################################################################################################################
|
|
-- ############# DCS - Target Impact Tracker Script #################
|
|
-- ############# by Draken35 #################
|
|
--###################################################################################################################################################
|
|
--[[ Requirements:
|
|
>>> MIST 4.5.107 or above <https://github.com/mrSkortch/MissionScriptingTools/tree/master>
|
|
|
|
--]]
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
--[[ Version history
|
|
2.0.00 03/04/2022 Development starts
|
|
2.0.01 05/03/2022 First release
|
|
2.0.02 05/04/2022 Added uncorrected impact position and warhead category to weaponsFire
|
|
2.1.00 05/10/2022 - Fired weapons grouping improvements
|
|
- Results API
|
|
- Added check on getAmmo to prevent ED bug ( getAmmo returns only 1 shell type for combat mix - A10C)
|
|
from crashing the script.
|
|
2.1.01 05/11/2022 - Fixed "no effect" message when target is killed by the weapon
|
|
(dead objects are not triggering hit event at this time)
|
|
2.1.02 05/12/2022 - Fixed missing shell count when using "combat mix" type of loads
|
|
2.2.00 05/15/2022 - Shell Burst recording
|
|
- new fired shell counting method / remove need for pass end
|
|
- added capture of shells display name and use it in the API if available
|
|
2.2.01 05/16/2022 - Added release data for last shell burst in pass
|
|
2.2.02 05/18/2022 - Added TargetImapct table to getGroupData
|
|
2.3.00 07/27/2022 - Added roll-in position tracking
|
|
- added polygonal zone targets types
|
|
2.3.01 09/10/2022 - Fixed issue with recording strafing hits with unlimited ammo (star shooting event is not longer populating weapon name - ED issue)
|
|
--]]
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------
|
|
--[[ Results API
|
|
|
|
count,{} = TITS.getWeponsFired(_unitName)
|
|
returns simple table of weapons IDs fired in the pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TITS.getWeponsInFlight()
|
|
returns simple table of weapons IDs fired in the pass
|
|
|
|
TITS.getWeaponData(WeaponID)
|
|
returns a complex object with the following data for the specific WeaponID:
|
|
weaponType
|
|
, releasePitch
|
|
, releaseYaw
|
|
, releaseRoll
|
|
, releaseHeading
|
|
, releaseSpeed
|
|
, releasePosition
|
|
, timeStamp
|
|
, inFlight
|
|
, unImpactPos -- uncorrected position
|
|
, avgImpactPos -- average corrected position
|
|
, group
|
|
, targetsHitList -- List of target hit by the weapon
|
|
, closestTargetHit -- complex object with:
|
|
{
|
|
target
|
|
, distance (from corrected position to target)
|
|
, bearing (true degrees, from corrected position to target)
|
|
, slantRange (from release position to target)
|
|
|
|
}
|
|
, TargetsHit -- complex object list with:
|
|
{
|
|
target
|
|
, distance (from corrected position to target)
|
|
, bearing (true degrees, from corrected position to target)
|
|
, slantRange (from release position to target)
|
|
|
|
}
|
|
|
|
TITS.getWeaponGroupsFired()
|
|
returns complex list of
|
|
{
|
|
groupId
|
|
, weaponType
|
|
, quantityFired
|
|
}
|
|
|
|
TITS.getWeaponGroupsFiredData()
|
|
returns complex list with
|
|
{
|
|
groupId
|
|
, weaponType
|
|
, quantityFired
|
|
, avgReleasePitch
|
|
, avgReleaseYaw
|
|
, avgReleaseRoll
|
|
, avgReleaseHeading
|
|
, avgReleaseSpeed
|
|
, avgReleasePosition
|
|
, groupPosition -- group center
|
|
, weaponList -- list of id of weapons in group
|
|
, targetsHitList -- List of target hit by the weapon
|
|
, closestTargetHit -- complex object with:
|
|
{
|
|
target
|
|
, distance (from group position to target)
|
|
, bearing (true degrees, from group position to target)
|
|
, slantRange (from avg release position to target)
|
|
|
|
}
|
|
, TargetsHit -- complex object list with:
|
|
{
|
|
target
|
|
, distance (from group position to target)
|
|
, bearing (true degrees, from group position to target)
|
|
, slantRange (from avg release position to target)
|
|
|
|
}
|
|
}
|
|
|
|
--]]
|
|
|
|
--[[ Pass Data return tables
|
|
Tables:
|
|
shellsFired -- initial count of rounds per shell type at pass Start, updated to rounds fired in the pass at pass End
|
|
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
|
|
, impactPos -- uncorrected
|
|
, group
|
|
}
|
|
|
|
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] = {
|
|
targetPosition
|
|
, impactPosition
|
|
, 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 #################
|
|
--###################################################################################################################################################
|
|
TITS.TargetTrackingInterval = 1 -- in seconds. Time in between target checks
|
|
TITS.WeaponTrackingInterval = 0.001 -- in seconds. Time in between weapon tracking checks
|
|
TITS.MaxMissDistance = 1000 -- meters. Any impact over this distance from the closest target will be considered as Miss
|
|
TITS.MaxCorrectionDistance = 15 -- meters
|
|
TITS.weaponGroupInterval = 1.5 -- in seconds. Minimun time between weapons shots to be considered a new group
|
|
|
|
--#################################################################################################################################################
|
|
-- ############# Initialization of globals ###############
|
|
--#################################################################################################################################################
|
|
TITS.TargetList = {}
|
|
TITS.PassData = {}
|
|
TITS.onPass = {}
|
|
TITS.lastOSclock = timer.getTime()
|
|
TITS.avgTrackTic = 0
|
|
|
|
|
|
--#################################################################################################################################################
|
|
-- ############# Event Handler & weapon tracking ###############
|
|
--#################################################################################################################################################
|
|
TITS.coreEventHandler = {}
|
|
function TITS.coreEventHandler: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_SHOT) and _event.initiator:getPlayerName() then -- id == 1 Shot
|
|
|
|
TITS.ShotEvent(_event)
|
|
|
|
elseif _event.id == world.event.S_EVENT_HIT and _event.initiator:getPlayerName() then -- id == 2 Hit
|
|
|
|
TITS.HitEvent(_event)
|
|
|
|
elseif _event.id == world.event.S_EVENT_SHOOTING_START and _event.initiator:getPlayerName() then -- id == 2 Hit
|
|
|
|
TITS.ShootingStart(_event)
|
|
|
|
elseif _event.id == world.event.S_EVENT_SHOOTING_END and _event.initiator:getPlayerName() then -- id == 2 Hit
|
|
|
|
TITS.ShootingEnd(_event)
|
|
|
|
end --if (_event.id == world.event.S_EVENT_SHOT) and _event.initiator:getPlayerName() then
|
|
-- ### End event processing
|
|
|
|
return true
|
|
end, _eventDCS) -- local status, err = pcall(function(_event)
|
|
|
|
if (not status) then
|
|
local msg = string.format("Target Impact Tracker (v%s): Error while handling event %s",TITS.Version, err)
|
|
env.error(msg,false)
|
|
end
|
|
end
|
|
--=============================================================================================================================================
|
|
function TITS.ShotEvent(_event)
|
|
local _weapon = _event.weapon
|
|
local _unitName = _event.initiator:getName()
|
|
local data = TITS.PassData[_unitName]
|
|
-- recond weapon fired and release data
|
|
|
|
if TITS.onPass[_unitName] then
|
|
|
|
if -- check for new group
|
|
(data.lastWPNfired ~= _weapon:getTypeName()) or
|
|
(timer.getTime() - data.lastWPNts) > TITS.weaponGroupInterval
|
|
then
|
|
data.lastWPNfired = _weapon:getTypeName()
|
|
data.lastWPNts = timer.getTime()
|
|
data.groupId = data.groupId + 1
|
|
data.groups[data.groupId] = {}
|
|
end
|
|
|
|
|
|
table.insert(data.groups[data.groupId],_weapon:getName())
|
|
|
|
|
|
local r = {
|
|
weaponType = _weapon:getTypeName()
|
|
, releasePitch = mist.utils.toDegree(mist.getClimbAngle(_event.initiator))
|
|
, releaseYaw = mist.utils.toDegree(mist.getYaw(_event.initiator))
|
|
, releaseRoll = mist.utils.toDegree(mist.getRoll(_event.initiator))
|
|
, releaseHeading = mist.utils.toDegree(mist.getHeading(_event.initiator))
|
|
, releaseSpeed = mist.vec.mag(_event.initiator:getVelocity())
|
|
, releasePosition = _event.initiator:getPosition().p
|
|
, timeStamp = timer.getTime()
|
|
, inFlight = true
|
|
, impactPos = {}
|
|
, groupId = data.groupId
|
|
}
|
|
|
|
data.weaponsFired[_weapon:getName()] = r
|
|
|
|
-- start tracking weapon
|
|
local params = {}
|
|
params.unitName = _event.initiator:getName()
|
|
params.launchPos = _event.initiator:getPosition().p
|
|
params.weapon = _weapon
|
|
params.weaponID = _weapon:getName()
|
|
params.weaponLastPoint = _weapon:getPoint()
|
|
params.weaponLastVelocity = _weapon:getVelocity()
|
|
|
|
TITS.lastOSclock = timer.getTime()
|
|
TITS.avgTrackTic = 0
|
|
timer.scheduleFunction(TITS.TrackWeapon, params, timer.getTime() + TITS.WeaponTrackingInterval )
|
|
end --if TITS.onPass[_unitName] then
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.HitEvent(_event)
|
|
local unitName = _event.initiator:getName()
|
|
local eventTarget = _event.target
|
|
local shellType = _event.weapon:getTypeName()
|
|
local isStrafingWeapon = string.find(_event.weapon:getTypeName():lower(), "weapons.shell")
|
|
local targetName = ''
|
|
local data = TITS.PassData[unitName]
|
|
|
|
if eventTarget then
|
|
targetName = eventTarget:getName()
|
|
end
|
|
|
|
if targetName ~= '' and TITS.onPass[unitName] then
|
|
if isStrafingWeapon then
|
|
local ts = data.shellHits[targetName]
|
|
if not ts then
|
|
target = TITS.TargetList[targetName]
|
|
if target then
|
|
if target.strafing then
|
|
data.shellHits[targetName] = {}
|
|
ts = data.shellHits[targetName]
|
|
end
|
|
end
|
|
end
|
|
if ts then
|
|
if ts[shellType] then
|
|
data.shellHits[targetName][shellType] = data.shellHits[targetName][shellType] + 1
|
|
else
|
|
data.shellHits[targetName][shellType] = 1
|
|
end
|
|
end
|
|
local desc = _event.weapon:getDesc()
|
|
if desc then
|
|
data.shellDisplayName[shellType] = desc.displayName
|
|
end
|
|
else -- it goes big boom
|
|
local target = TITS.getTargetObj(targetName)
|
|
if target then
|
|
-- record the target that got splashed by the weapon
|
|
local t = TITS.TargetList[targetName]
|
|
if t.bda then
|
|
if not data.weaponHits[_event.weapon:getName()] then
|
|
data.weaponHits[_event.weapon:getName()] = {}
|
|
end
|
|
|
|
data.weaponHits[_event.weapon:getName()][targetName] = timer.getTime()
|
|
end -- if t.bda then
|
|
end -- if target then
|
|
end -- if isStrafingWeapon then
|
|
end -- if targetName ~= '' and TITS.onPass[unitName] then
|
|
|
|
return true
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.ShootingStart(_event)
|
|
local _unitName = _event.initiator:getName()
|
|
local unit = Unit.getByName(_unitName)
|
|
local data = TITS.PassData[_unitName]
|
|
local ammoTable = unit:getAmmo()
|
|
|
|
for _,v in pairs (ammoTable) do
|
|
local t = v.desc
|
|
if string.find(t.typeName:lower(), "weapons.shell") then
|
|
|
|
if _event.weapon_name then
|
|
if data.shellsFired[_event.weapon_name] then
|
|
data.shellsFired[_event.weapon_name].init = v.count
|
|
else
|
|
data.shellsFired[_event.weapon_name] = {
|
|
init = v.count
|
|
, fired = 0
|
|
}
|
|
end
|
|
else
|
|
if data.shellsFired[t.typeName] then
|
|
data.shellsFired[t.typeName].init = v.count
|
|
else
|
|
data.shellsFired[t.typeName] = {
|
|
init = v.count
|
|
, fired = 0
|
|
}
|
|
end
|
|
end -- if _event.weapon_name then
|
|
end--if string.find(t.typeName:lower(), "weapons.shell") then
|
|
end -- for _,v in pairs (ammoTable) do
|
|
|
|
|
|
data.LBreleasePitch = mist.utils.toDegree(mist.getClimbAngle(_event.initiator))
|
|
data.LBreleaseYaw = mist.utils.toDegree(mist.getYaw(_event.initiator))
|
|
data.LBreleaseRoll = mist.utils.toDegree(mist.getRoll(_event.initiator))
|
|
data.LBreleaseHeading = mist.utils.toDegree(mist.getHeading(_event.initiator))
|
|
data.LBreleaseSpeed = mist.vec.mag(_event.initiator:getVelocity())
|
|
data.LBreleasePosition = _event.initiator:getPosition().p
|
|
|
|
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.ShootingEnd(_event)
|
|
local _unitName = _event.initiator:getName()
|
|
local unit = Unit.getByName(_unitName)
|
|
local data = TITS.PassData[_unitName]
|
|
local ammoTable = unit:getAmmo()
|
|
local shellFounds = false
|
|
|
|
data.lastShellFired = timer.getTime()
|
|
|
|
if ammoTable then
|
|
for _,v in pairs (ammoTable) do
|
|
local t = v.desc
|
|
if string.find(t.typeName:lower(), "weapons.shell") then
|
|
shellFounds = true
|
|
d = data.shellsFired[t.typeName]
|
|
data.shellDisplayName[t.typeName] = t.displayName
|
|
if d then
|
|
d.fired = d.init - v.count
|
|
end
|
|
end
|
|
end -- for _,v in pairs (ammoTable) do
|
|
end
|
|
|
|
if not ammoTable or not shellFounds then
|
|
for _,d in pairs(data.shellsFired) do
|
|
d.fired = d.init
|
|
end
|
|
end
|
|
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.TrackWeapon(params,time)
|
|
local OSclock = timer.getTime()
|
|
TITS.avgTrackTic = (TITS.avgTrackTic + (OSclock-TITS.lastOSclock))/2
|
|
TITS.lastOSclock = OSclock
|
|
|
|
local _status,_weaponData, _weaponVel = pcall(function()
|
|
return {params.weapon:getPoint(),params.weapon:getVelocity()}
|
|
end)
|
|
if _status then
|
|
params.weaponLastPoint = _weaponData[1]
|
|
params.weaponLastVelocity = _weaponData[2]
|
|
return timer.getTime() + TITS.WeaponTrackingInterval -- keep tracking
|
|
else -- if _status then (weapon not longer exists)
|
|
local data = TITS.PassData[params.unitName]
|
|
data.lastImpact = timer.getTime()
|
|
|
|
data.weaponsFired[params.weaponID].inFlight = false
|
|
for targetName,targetData in pairs (TITS.TargetList) do
|
|
if targetData.detected and targetData.impact then
|
|
if (not targetData.dead) or ((targetData.dead) and ((targetData.dead >= data.timestamp))) then
|
|
local targetPos = TITS.getTargetPos(targetName)
|
|
local targetVel = TITS.getTargetVel(targetName)
|
|
local ImpactPos = params.weaponLastPoint
|
|
data.weaponsFired[params.weaponID].impactPos = ImpactPos
|
|
local weaponVel = params.weaponLastVelocity
|
|
local impactDistance = mist.utils.get3DDist(ImpactPos,targetPos)
|
|
if impactDistance <= TITS.MaxMissDistance then
|
|
|
|
ImpactPos = mist.vec.add(ImpactPos ,mist.vec.scalar_mult(weaponVel, TITS.WeaponTrackingInterval ))
|
|
|
|
if impactDistance <= TITS.MaxCorrectionDistance then -- weapon hit the target's collision box, correct the impact
|
|
if params.weaponLastVelocity.y ~= 0 then
|
|
-- correct Weapon and Target position
|
|
local deltaY = math.abs(ImpactPos.y - targetPos.y)
|
|
local flightTime = math.abs(deltaY / weaponVel.y)
|
|
ImpactPos = mist.vec.add(ImpactPos ,mist.vec.scalar_mult(weaponVel , flightTime ))
|
|
targetPos = mist.vec.add(targetPos ,mist.vec.scalar_mult(targetVel, flightTime ))
|
|
end -- params.weaponLastVelocity.y ~= 0
|
|
impactDistance = mist.utils.get3DDist(ImpactPos,targetPos)
|
|
|
|
end -- if impactDistance <= TITS.MaxCorrectionDistance then
|
|
|
|
-- check if the target is dead, and if so, record a hit
|
|
if TITS.getTargetLife(targetName) == 0 then
|
|
|
|
if not data.weaponHits[params.weaponID] then
|
|
data.weaponHits[params.weaponID] = {}
|
|
end
|
|
data.weaponHits[params.weaponID][targetName] = timer.getTime()
|
|
end
|
|
--record the impact
|
|
if not data.impactData[params.weaponID] then
|
|
data.impactData[params.weaponID] = {}
|
|
end
|
|
|
|
data.impactData[params.weaponID][targetName] = {
|
|
targetPos = targetPos
|
|
, impactPos = ImpactPos
|
|
, impactDistance = impactDistance
|
|
, timestamp = timer.getTime()
|
|
}
|
|
-- see if target is a zone and the impact is with the radius
|
|
if targetData.type == 'zone' and targetData.bda then
|
|
local zone = trigger.misc.getZone(targetName)
|
|
if impactDistance <= zone.radius then
|
|
|
|
if not data.weaponHits[params.weaponID] then
|
|
data.weaponHits[params.weaponID] = {}
|
|
end
|
|
data.weaponHits[params.weaponID][targetName] = timer.getTime()
|
|
end
|
|
end -- if targetData.type = 'zone' then
|
|
if targetData.type == 'poly' and targetData.bda then
|
|
if mist.pointInPolygon(ImpactPos,TITS.getTargetObj(targetName), 1000) then
|
|
|
|
if not data.weaponHits[params.weaponID] then
|
|
data.weaponHits[params.weaponID] = {}
|
|
end
|
|
data.weaponHits[params.weaponID][targetName] = timer.getTime()
|
|
end
|
|
end -- if targetData.type = 'poly' then
|
|
|
|
end -- if impactDistance <= TITS.MaxMissDistance then
|
|
end --if (not targetData.dead) or ((targetData.dead) and ((targetData.dead >= data.timestamp))) then
|
|
end -- if targetData.detected and targetData.impact then
|
|
end -- for targetName,targetData in pairs (TITS.TargetList) do
|
|
end -- if _status then
|
|
|
|
end -- function
|
|
--
|
|
--#################################################################################################################################################
|
|
-- ############# target functions ###############
|
|
--#################################################################################################################################################
|
|
--
|
|
function TITS.AddTarget(
|
|
_TargetName -- UNIQUE! Target name
|
|
, _DisplayName -- display name, accepts duplicates, if empty _TargetName will be used
|
|
, _Type -- [ 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
|
|
, _TrackImpact -- use target to calculate distance to impact
|
|
, _TrackStrafing -- count strafing hits on target
|
|
, _ReportHits -- reports splash damage from weapons
|
|
, _desZone -- designation zone
|
|
, _AllowSmokeDesignation -- Allow use of smoke designation for target
|
|
, _AllowLaserDesignation -- Allow use of laser designation for target
|
|
, _AllowIRDesignation -- Allow use of IR designation for target
|
|
, _AllowNoMarkDesignation -- Allow use of target designation with no marks (provides coordinates)
|
|
, _AllowListCoordinates -- 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
|
|
)
|
|
|
|
local dn = _DisplayName
|
|
if dn == '' then
|
|
dn = _TargetName
|
|
end
|
|
|
|
local life0 = 100
|
|
if _Type == 'static' then
|
|
local obj = StaticObject.getByName(_TargetName)
|
|
if obj then
|
|
life0 = obj:getLife()
|
|
end
|
|
end
|
|
|
|
local gpn = ''
|
|
if _Type == 'unit' then
|
|
local u = Unit.getByName(_TargetName)
|
|
gpn = u:getGroup():getName()
|
|
end
|
|
|
|
|
|
local v = {
|
|
name = _TargetName,
|
|
displayName = dn,
|
|
type = _Type,
|
|
respawn = _Respawn,
|
|
impact = _TrackImpact,
|
|
strafing = _TrackStrafing,
|
|
bda = _ReportHits,
|
|
des_zone = _desZone,
|
|
des_wp = _AllowSmokeDesignation,
|
|
des_laser = _AllowLaserDesignation,
|
|
des_ir = _AllowIRDesignation,
|
|
des_nomark = _AllowNoMarkDesignation,
|
|
list_coor = _AllowListCoordinates,
|
|
uiNum1 = _uiNum1,
|
|
uiNum2 = _uiNum2,
|
|
uiNum3 = _uiNum3,
|
|
uiBool1 = _uiBool1,
|
|
uiBool2 = _uiBool2,
|
|
uiBool3 = _uiBool3,
|
|
uiText1 = _uiText1,
|
|
uiText2 = _uiText2,
|
|
uiText3 = _uiText3,
|
|
|
|
|
|
-- internal use attributes
|
|
detected = nil,-- Detected = target detected as alive
|
|
dead = nil,-- Dead = dead detection time
|
|
life0 = life0 , -- initial life for statics & zones
|
|
groupName = gpn,
|
|
lastPos = {},
|
|
lastVel = {}
|
|
}
|
|
TITS.TargetList[_TargetName] = v
|
|
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.respawnTarget(_TargetName, _override)
|
|
local tgt = TITS.TargetList[_TargetName]
|
|
|
|
if tgt then
|
|
if tgt.type == 'unit' and (tgt.respawn or _override) and tgt.dead then
|
|
if not Group.getByName(tgt.groupName) then
|
|
mist.respawnGroup(tgt.groupName, true)
|
|
tgt.dead = nil
|
|
tgt.detected = timer.getTime()
|
|
end
|
|
end
|
|
end
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getTargetObj(_TargetName)
|
|
local tgt = TITS.TargetList[_TargetName]
|
|
local obj = nil
|
|
|
|
if tgt.type == 'unit' then
|
|
obj = Unit.getByName(_TargetName)
|
|
elseif tgt.type == 'static' then
|
|
obj = StaticObject.getByName(_TargetName)
|
|
elseif tgt.type == 'zone' then
|
|
obj = trigger.misc.getZone(_TargetName)
|
|
elseif tgt.type == 'poly' then
|
|
local u = Unit.getByName(_TargetName)
|
|
local g = u:getGroup()
|
|
obj = mist.getGroupPoints(g:getName())
|
|
end
|
|
return obj
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getTargetLife(_TargetName)
|
|
local tgt = TITS.TargetList[_TargetName]
|
|
local obj = TITS.getTargetObj(_TargetName)
|
|
local life = 0
|
|
|
|
if obj then
|
|
if tgt.type == 'unit' or tgt.type == 'static' then
|
|
life = obj:getLife()
|
|
elseif tgt.type == 'zone' then
|
|
life = 100
|
|
elseif tgt.type == 'poly' then
|
|
life = 100
|
|
end
|
|
end
|
|
return life
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getMaxTargetLife(_TargetName)
|
|
local tgt = TITS.TargetList[_TargetName]
|
|
local obj = TITS.getTargetObj(_TargetName)
|
|
local life = 0
|
|
|
|
if obj then
|
|
if tgt.type == 'unit' then
|
|
life = obj:getLife0()
|
|
elseif tgt.type == 'static' or tgt.type == 'zone' then
|
|
life = tgt.life0
|
|
end
|
|
end
|
|
return life
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.targetExists(_TargetName)
|
|
local tgt = TITS.TargetList[_TargetName]
|
|
local obj = TITS.getTargetObj(_TargetName)
|
|
local exists = false
|
|
|
|
if obj then
|
|
if tgt.type == 'unit' or tgt.type == 'static' then
|
|
exists = obj:isExist()
|
|
elseif tgt.type == 'zone' then
|
|
exists = true
|
|
elseif tgt.type == 'poly' then
|
|
exists = true
|
|
end
|
|
end
|
|
return exists
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getTargetPos(_TargetName)
|
|
local tgt = TITS.TargetList[_TargetName]
|
|
local obj = TITS.getTargetObj(_TargetName)
|
|
local Pos = nil
|
|
|
|
if obj then
|
|
if tgt.type == 'unit' or tgt.type == 'static' then
|
|
Pos = obj:getPoint()
|
|
elseif tgt.type == 'zone' then
|
|
Pos = mist.utils.makeVec3GL(obj.point)
|
|
elseif tgt.type == 'poly' then
|
|
local n = 0
|
|
Pos = {x=0,y=0,z=0}
|
|
for _,p in pairs(obj) do
|
|
n = n + 1
|
|
Pos = mist.vec.add(Pos, mist.utils.makeVec3GL(p))
|
|
end
|
|
if n > 0 then
|
|
Pos = mist.vec.scalar_mult(Pos , 1 / n)
|
|
end
|
|
|
|
end
|
|
else -- if obj then
|
|
-- target is dead
|
|
Pos = tgt.lastPos
|
|
end -- if obj then
|
|
return Pos
|
|
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getTargetVel(_TargetName)
|
|
local tgt = TITS.TargetList[_TargetName]
|
|
local obj = TITS.getTargetObj(_TargetName)
|
|
local Vel = nil
|
|
|
|
if obj then
|
|
if tgt.type == 'unit' or tgt.type == 'static' then
|
|
Vel = obj:getVelocity()
|
|
elseif tgt.type == 'zone' then
|
|
Vel = { x=0, y=0, z=0}
|
|
elseif tgt.type == 'poly' then
|
|
Vel = { x=0, y=0, z=0}
|
|
end
|
|
else -- if obj then
|
|
-- target is dead
|
|
Vel = tgt.lastVel
|
|
end -- if obj then
|
|
return Vel
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.trackTargets(_params,time)
|
|
for k,v in pairs(TITS.TargetList) do
|
|
if v.type ~='zone' and v.type ~='poly' then
|
|
local o = TITS.getTargetObj(k)
|
|
local life = 0
|
|
if o then
|
|
life = TITS.getTargetLife(k)
|
|
end
|
|
if life > 0 then
|
|
-- target is alive
|
|
if v.detected == nil then
|
|
-- target detected
|
|
v.detected = timer.getTime()
|
|
end -- if v.Detected == nil then
|
|
-- if v.lastPos == nil or v.lastVel == nil or v.canMove then
|
|
if v.type == 'unit' then
|
|
v.lastPos = TITS.getTargetPos(k)
|
|
v.lastVel = TITS.getTargetVel(k)
|
|
end --if v.type == 'unit' then
|
|
-- end -- if v.lastPos == nil or v.lastVel == nil or v.canMove then
|
|
else -- if o then
|
|
-- target is dead
|
|
if v.detected and v.dead == nil then
|
|
v.dead = timer.getTime( )
|
|
end -- if v.Detected and v.Dead == nil then
|
|
end -- if o then
|
|
else
|
|
if v.detected == nil then
|
|
-- target detected
|
|
v.detected = timer.getTime()
|
|
end -- if v.Detected == nil then
|
|
end -- if v.type ~='zone' then
|
|
end -- for k,v in pairs(TITS.TargetList) do
|
|
|
|
return timer.getTime() + TITS.TargetTrackingInterval -- keep tracking
|
|
end -- function
|
|
--
|
|
--#################################################################################################################################################
|
|
-- ############# Pass functions ###############
|
|
--#################################################################################################################################################
|
|
--
|
|
function TITS.passInit(_unitName)
|
|
local unit = Unit.getByName(_unitName)
|
|
TITS.PassData[_unitName] = {}
|
|
|
|
local r = {
|
|
shellsFired = {} -- initial count of rounds per shell type at pass Start, updated to rounds fired in the pass at pass End
|
|
, shellHits = {} -- per target per shell type
|
|
, shellDisplayName = {} -- display name per shell type
|
|
, weaponsFired = {} -- and their release params & designated target at the time, updated by shot event if TITS.onPass[_unitName] == true
|
|
, weaponHits = {} -- per target per weapon , updated by hit event if TITS.onPass[_unitName] == true
|
|
, impactData = {} -- per target per weapon & slant range from launch position
|
|
, targetHealth = {} -- per target at this time
|
|
, timestamp = timer.getTime() -- Pass start time
|
|
, groupId = 0 -- fired weapons group counter
|
|
, lastWPNfired = '' -- last weapon TYPE fired
|
|
, lastWPNts = 0 -- Last weapon fired time stamp
|
|
, groups = {} -- groups indexing
|
|
, lastShellFired = timer.getTime() -- to control Auto pass end in the UI
|
|
, lastImpact = timer.getTime() -- to control Auto pass end in the UI
|
|
, LBreleasePitch = 0
|
|
, LBreleaseYaw = 0
|
|
, LBreleaseRoll = 0
|
|
, LBreleaseHeading = 0
|
|
, LBreleaseSpeed = {}
|
|
, LBreleasePosition = {}
|
|
, rollInPosition = unit:getPosition().p
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
-- init shellsFired
|
|
--[[
|
|
local unit = Unit.getByName(_unitName)
|
|
local ammoTable = unit:getAmmo()
|
|
for _,v in pairs (ammoTable) do
|
|
local t = v.desc
|
|
if string.find(t.typeName:lower(), "weapons.shell") then
|
|
local c = { init = v.count, fired = 0 }
|
|
r.shellsFired[t.typeName] = c
|
|
end
|
|
end -- for _,v in pairs (ammoTable) do
|
|
]]--
|
|
-- init targetHealth & shellHits
|
|
for k,targetData in pairs (TITS.TargetList) do
|
|
local th = TITS.getTargetLife(k)
|
|
local h = {
|
|
start_health = th
|
|
, end_health = th
|
|
}
|
|
r.targetHealth[k] = h
|
|
|
|
--[[ shellHits
|
|
if targetData.strafing and targetData.type ~= 'zone' then --and TITS.getTargetLife(k) > 0 then
|
|
r.shellHits[k] = {}
|
|
for v,_ in pairs(r.shellsFired) do
|
|
r.shellHits[k][v] = 0
|
|
end -- for v,_ in pairs(r.shellsFired) do
|
|
end -- if k.strafing then
|
|
--]]
|
|
end -- for k,_ in pairs (TITS.TargetList) do
|
|
|
|
TITS.PassData[_unitName] = r
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.passStart(_unitName)
|
|
TITS.onPass[_unitName] = true
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.passEnd(_unitName)
|
|
local weponsInFlight = false
|
|
local d = TITS.PassData[_unitName]
|
|
|
|
if d.weaponsFired then
|
|
for _,y in pairs(d.weaponsFired) do
|
|
weponsInFlight = weponsInFlight or y.inFlight
|
|
end -- for _,y in pairs(d.weaponsFired) do
|
|
end
|
|
if not weponsInFlight then
|
|
TITS.onPass[_unitName] = false
|
|
-- update PassData shellsFired
|
|
local unit = Unit.getByName(_unitName)
|
|
local ammoTable = unit:getAmmo()
|
|
local r = d.shellsFired
|
|
local ShellsFounds = false
|
|
if ammoTable then
|
|
for _,v in pairs (ammoTable) do
|
|
local t = v.desc
|
|
if string.find(t.typeName:lower(), "weapons.shell") then
|
|
ShellsFounds = true
|
|
local c = r[t.typeName]
|
|
if c then
|
|
c.fired = c.init - v.count -- rounds fired
|
|
else
|
|
if r[1] then
|
|
r[1].fired = r[1].init - v.count
|
|
else
|
|
r[t.typeName] = {fired = 0}
|
|
end
|
|
local msg = string.format("Target Impact Tracker (v%s): %s ammo type load error",TITS.Version, t.typeName)
|
|
env.error(msg,false)
|
|
end
|
|
end
|
|
end -- for _,v in pairs (ammoTable) do
|
|
end
|
|
if not ammoTable or not ShellsFounds then
|
|
-- all anmo was used
|
|
for _,v in pairs(r) do
|
|
v.fired = v.init
|
|
end
|
|
end
|
|
|
|
-- update target health
|
|
for k,targetData in pairs (TITS.TargetList) do
|
|
local th = TITS.getTargetLife(k)
|
|
local h = d.targetHealth[k]
|
|
h.end_health = th
|
|
d.targetHealth[k] = h
|
|
end -- for k,targetData in pairs (TITS.TargetList) do
|
|
end
|
|
return not weponsInFlight
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.passGetData(_unitName)
|
|
local r = TITS.PassData
|
|
if r ~= {} then
|
|
r = TITS.PassData[_unitName]
|
|
end
|
|
return r
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.weaponsInFlight(_unitName)
|
|
local weponsInFlight = false
|
|
local d = TITS.PassData[_unitName]
|
|
|
|
if d.weaponsFired then
|
|
for _,y in pairs(d.weaponsFired) do
|
|
weponsInFlight = weponsInFlight or y.inFlight
|
|
end -- for _,y in pairs(d.weaponsFired) do
|
|
end
|
|
return weponsInFlight
|
|
end -- function
|
|
--
|
|
--#################################################################################################################################################
|
|
-- ############# Results API ###############
|
|
--#################################################################################################################################################
|
|
--
|
|
function TITS.getWeponsFired(_unitName)
|
|
local wftable = {}
|
|
local count = 0
|
|
|
|
|
|
local data = TITS.passGetData(_unitName)
|
|
if TITS.rowCount(data) > 0 then
|
|
local wf = data.weaponsFired
|
|
local r = {}
|
|
|
|
if TITS.rowCount(wf) > 0 then
|
|
for wID,_ in pairs(wf) do
|
|
count = count + 1
|
|
wftable[wID] = wID
|
|
end
|
|
end
|
|
|
|
end --if data ~= {} then
|
|
r = {count = count, list = wftable}
|
|
return r
|
|
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getWeaponData(_unitName, _weaponID)
|
|
--[[
|
|
weaponType
|
|
, releasePitch
|
|
, releaseYaw
|
|
, releaseRoll
|
|
, releaseHeading
|
|
, releaseSpeed
|
|
, releasePosition
|
|
, inFlight
|
|
, impactPos -- uncorrected
|
|
, group
|
|
, impacts[target]
|
|
target
|
|
, targetPos
|
|
, hit
|
|
, distance
|
|
, impactPos -- corrected
|
|
, closestTarget
|
|
, miss -- no impacts close to targets
|
|
, avgImpactPos -- corrected
|
|
, targetsHit
|
|
, rollInDistance -- to closest target
|
|
, rollInAltitudeAT -- Altitude above closest target
|
|
--]]
|
|
local data = TITS.passGetData(_unitName)
|
|
local wData = data.weaponsFired[_weaponID]
|
|
local wHits= data.weaponHits[_weaponID]
|
|
local r = {}
|
|
if wData then
|
|
r = {
|
|
weaponType = wData.weaponType
|
|
, releasePitch = wData.releasePitch
|
|
, releaseYaw = wData.releaseYaw
|
|
, releaseRoll = wData.releaseRoll
|
|
, releaseHeading = wData.releaseHeading
|
|
, releaseSpeed = wData.releaseSpeed
|
|
, releasePosition = wData.releasePosition
|
|
, inFlight = wData.inFlight
|
|
, impactPos = wData.impactPos -- uncorrected
|
|
, group = wData.group
|
|
, impacts = {}
|
|
, closestTarget = ''
|
|
, miss = true
|
|
, avgImpactPos = {x=0,y=0,z=0}
|
|
, targetsHit = {}
|
|
, rollInDistance = 0
|
|
, rollInAltitudeAT = 0
|
|
}
|
|
-- get impacts and find closest target
|
|
local impactData = data.impactData[_weaponID]
|
|
local minDist = TITS.MaxMissDistance * 2
|
|
|
|
if impactData then
|
|
r.miss = false
|
|
local impactCount = 0
|
|
for target, irow in pairs(impactData) do
|
|
local hit = false
|
|
impactCount = impactCount + 1
|
|
r.avgImpactPos = mist.vec.add(r.avgImpactPos , irow.impactPos)
|
|
if irow.impactDistance <= minDist then
|
|
minDist = irow.impactDistance
|
|
r.closestTarget = target
|
|
end
|
|
if wHits then
|
|
if wHits[target] then
|
|
hit = true
|
|
table.insert(r.targetsHit,target)
|
|
end
|
|
end
|
|
r.impacts[target] = {
|
|
target = target
|
|
, targetPos = irow.targetPos
|
|
, hit = hit
|
|
, distance = irow.impactDistance
|
|
, impactPos = irow.impactPos
|
|
}
|
|
|
|
end -- for target, irow in pairs(impactData)
|
|
r.avgImpactPos = mist.vec.scalar_mult(r.avgImpactPos , 1/impactCount)
|
|
if r.closestTarget ~= '' then
|
|
local tp = TITS.getTargetPos(r.closestTarget)
|
|
r.rollInDistance = mist.utils.get2DDist(data.rollInPosition,tp)
|
|
r.rollInAltitudeAT = data.rollInPosition.y - tp.y
|
|
end
|
|
end -- if impactData then
|
|
|
|
end -- if wData then
|
|
|
|
return r
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getShellsFired(_unitName)
|
|
local shtable = {}
|
|
local total = 0
|
|
local data = TITS.passGetData(_unitName)
|
|
if TITS.rowCount(data) > 0 then
|
|
local sf = data.shellsFired
|
|
local r = {}
|
|
|
|
if TITS.rowCount(sf) > 0 then
|
|
for shellType,s in pairs(sf) do
|
|
if s.fired then
|
|
total = total + s.fired
|
|
shtable[shellType] = s.fired
|
|
end
|
|
end
|
|
end
|
|
|
|
end --if data ~= {} then
|
|
r = {total = total, list = shtable, shootHeading = data.LBreleaseHeading}
|
|
return r
|
|
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getShellHits(_unitName)
|
|
local shtable = {}
|
|
local targets = {}
|
|
local r = {}
|
|
local total = 0
|
|
local data = TITS.passGetData(_unitName)
|
|
if TITS.rowCount(data) > 0 then
|
|
local sh = data.shellHits
|
|
|
|
if TITS.rowCount(sh) > 0 then
|
|
for target,sData in pairs(sh) do
|
|
if not TITS.inTable(target,targets) then
|
|
table.insert(targets,target)
|
|
end
|
|
for shellType,count in pairs(sData) do
|
|
if count > 0 then
|
|
total = total + count
|
|
if data.shellDisplayName[shellType] then
|
|
table.insert(shtable, {target = target, shellType = data.shellDisplayName[shellType], hits = count})
|
|
else
|
|
shellType = shellType:gsub("weapons.shells.", "")
|
|
table.insert(shtable, {target = target, shellType = shellType, hits = count})
|
|
end
|
|
end
|
|
end -- for shellType,count in pairs(sData) do
|
|
end -- for target,sData in pairs(sh) do
|
|
end
|
|
end --if data ~= {} then
|
|
r = {total = total, targets = targets, list = shtable}
|
|
return r
|
|
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.getGroups(_unitName)
|
|
local grptable = {}
|
|
local count = 0
|
|
local r = {}
|
|
local data = TITS.passGetData(_unitName)
|
|
|
|
if TITS.rowCount(data) > 0 then
|
|
local groups = data.groups
|
|
|
|
|
|
if TITS.rowCount(groups) > 0 then
|
|
for gID,_ in pairs(groups) do
|
|
count = count + 1
|
|
grptable[gID] = gID
|
|
end
|
|
end
|
|
|
|
end --if data ~= {} then
|
|
r = {count = count, list = grptable}
|
|
return r
|
|
|
|
end --function
|
|
--=============================================================================================================================================
|
|
function TITS.getGroupData(_unitName, _groupID)
|
|
--[[
|
|
GroupID
|
|
, weaponType
|
|
, releasePitch
|
|
, releaseYaw
|
|
, releaseRoll
|
|
, releaseHeading
|
|
, releaseSpeed
|
|
, releasePosition
|
|
, impactPos -- Average from weapon impacts
|
|
, weapons {id list}
|
|
, weaponPos {avgImpactPos list}
|
|
, closestTarget
|
|
, closestTargetPos
|
|
, closestTargetHit
|
|
, miss -- no impacts close to targets
|
|
, avgImpactPos -- corrected
|
|
, targetsHit
|
|
, size (max dist )
|
|
, TargetImpacts
|
|
|
|
--]]
|
|
local r = {}
|
|
local data = TITS.passGetData(_unitName)
|
|
|
|
if TITS.rowCount(data) > 0 then
|
|
local weapons = data.groups[_groupID]
|
|
local impactCount = 0
|
|
local weaponCount = 0
|
|
local targetData = {}
|
|
|
|
r = {
|
|
groupId = _groupID
|
|
, weaponType = ''
|
|
, weaponCount = 0
|
|
, releasePitch = 0
|
|
, releaseYaw = 0
|
|
, releaseRoll = 0
|
|
, releaseHeading = 0
|
|
, releaseSpeed = 0
|
|
, releasePosition = {x=0,y=0,z=0}
|
|
, impactPos = {x=0,y=0,z=0} -- center
|
|
, weapons = weapons
|
|
, weaponsPos = {}
|
|
, closestTarget = ''
|
|
, closestTargetPos = {}
|
|
, closestTargetHit = false
|
|
, closestTargetDist = 2 * TITS.MaxMissDistance
|
|
, closestTargetHitCount = 0
|
|
, miss = true
|
|
, avgImpactPos = {x=0,y=0,z=0}
|
|
, targetsHit = {}
|
|
, size = 0
|
|
, TargetImpacts ={}
|
|
, rollInDistance = 0
|
|
, rollInAltitudeAT = 0
|
|
|
|
}
|
|
|
|
for _, weaponID in pairs(weapons) do
|
|
local wd = TITS.getWeaponData(_unitName, weaponID)
|
|
|
|
weaponCount = weaponCount + 1
|
|
r.weaponType = wd.weaponType
|
|
r.releasePitch = r.releasePitch + wd.releasePitch
|
|
r.releaseYaw = r.releaseYaw + wd.releaseYaw
|
|
r.releaseRoll = r.releaseRoll + wd.releaseRoll
|
|
r.releaseHeading = r.releaseHeading + wd.releaseHeading
|
|
r.releaseSpeed = r.releaseSpeed + wd.releaseSpeed
|
|
r.releasePosition = mist.vec.add(r.releasePosition,wd.releasePosition)
|
|
r.impactPos = mist.vec.add(r.impactPos,wd.avgImpactPos)
|
|
table.insert(r.weaponsPos,wd.avgImpactPos)
|
|
|
|
if not wd.miss then
|
|
r.miss = false
|
|
for _,impact in pairs(wd.impacts) do
|
|
table.insert(targetData,impact)
|
|
if impact.hit and not TITS.inTable(impact.target,r.targetsHit) then
|
|
table.insert(r.targetsHit,impact.target)
|
|
end
|
|
end --for _,impact in pairs(wd.impacts) do
|
|
end -- if not wd.miss then
|
|
|
|
end -- for _,weaponID in pairs(weapons) do
|
|
r.TargetImpacts = targetData
|
|
-- averages
|
|
r.weaponCount = weaponCount
|
|
if weaponCount > 0 then
|
|
r.releasePitch = r.releasePitch / weaponCount
|
|
r.releaseYaw = r.releaseYaw / weaponCount
|
|
r.releaseRoll = r.releaseRoll / weaponCount
|
|
r.releaseHeading = r.releaseHeading / weaponCount
|
|
r.releaseSpeed = r.releaseSpeed / weaponCount
|
|
r.releasePosition = mist.vec.scalar_mult(r.releasePosition , 1/weaponCount)
|
|
r.impactPos = mist.vec.scalar_mult(r.impactPos , 1/weaponCount)
|
|
if not r.miss then
|
|
-- get closestTarget
|
|
for _,t in pairs(targetData) do
|
|
local d = mist.utils.get3DDist(r.impactPos, t.targetPos)
|
|
if d <= r.closestTargetDist then
|
|
r.closestTargetDist = d
|
|
r.closestTarget = t.target
|
|
r.closestTargetPos = t.targetPos
|
|
r.closestTargetHit = t.hit
|
|
end
|
|
end -- for _,t in pairs(targetData) do
|
|
|
|
if r.closestTarget ~= '' then
|
|
local tp = TITS.getTargetPos(r.closestTarget)
|
|
r.rollInDistance = mist.utils.get2DDist(data.rollInPosition,tp)
|
|
r.rollInAltitudeAT = data.rollInPosition.y - tp.y
|
|
end
|
|
|
|
end -- if not r.miss then
|
|
--get size
|
|
for i = 1, weaponCount-1, 1 do
|
|
for j = i+1, weaponCount, 1 do
|
|
local d = mist.utils.get3DDist(r.weaponsPos[i],r.weaponsPos[j])
|
|
if d > r.size then
|
|
r.size = d
|
|
end
|
|
end --for j = i+1, weaponCount, 1 do
|
|
end -- for i = 1, weaponCount, 1 do
|
|
|
|
for _, weaponID in pairs(weapons) do
|
|
local wd = TITS.getWeaponData(_unitName, weaponID)
|
|
if TITS.inTable(r.closestTarget,wd.targetsHit) then
|
|
r.closestTargetHitCount = r.closestTargetHitCount + 1
|
|
end
|
|
end --for _, weaponID in pairs(weapons) do
|
|
|
|
end -- if weaponCount > 0 then
|
|
|
|
|
|
end --if data ~= {} then
|
|
|
|
return r
|
|
|
|
end --function
|
|
--
|
|
--#################################################################################################################################################
|
|
-- ############# utils ###############
|
|
--#################################################################################################################################################
|
|
--
|
|
function TITS.rowCount(t)
|
|
local r = 0
|
|
if t then
|
|
for _,_ in pairs(t) do
|
|
r = r + 1
|
|
end
|
|
end
|
|
return r
|
|
end -- function
|
|
--=============================================================================================================================================
|
|
function TITS.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(TITS.coreEventHandler)
|
|
-- start tracking the targets
|
|
timer.scheduleFunction(TITS.trackTargets,{},timer.getTime() + TITS.TargetTrackingInterval)
|
|
|
|
trigger.action.outText( 'Target Impact Tracker Script- v'..TITS.Version, 1 )
|
|
end |