WEAPON v0.1.0

**WEAPON**
- Improments of class
- ARTY works
- FOX works
- RANGE should work, not tested.
This commit is contained in:
Frank 2023-02-04 23:28:45 +01:00
parent 4b4708e2a8
commit 6d967358da
7 changed files with 462 additions and 236 deletions

View File

@ -1133,12 +1133,15 @@ do -- COORDINATE
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
-- @param #COORDINATE self
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
-- @return DCS#Distance Distance The distance in meters.
function COORDINATE:Get3DDistance( TargetCoordinate )
local TargetVec3 = TargetCoordinate:GetVec3()
--local TargetVec3 = TargetCoordinate:GetVec3()
local TargetVec3 = {x=TargetCoordinate.x, y=TargetCoordinate.y, z=TargetCoordinate.z}
local SourceVec3 = self:GetVec3()
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
--local dist=( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
local dist=UTILS.VecDist3D(TargetVec3, SourceVec3)
return dist
end

View File

@ -103,6 +103,7 @@
-- @field #number coalition The coalition of the arty group.
-- @field #boolean respawnafterdeath Respawn arty group after all units are dead.
-- @field #number respawndelay Respawn delay in seconds.
-- @field #number dtTrack Time interval in seconds for weapon tracking.
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can
@ -693,7 +694,7 @@ ARTY.db={
--- Arty script version.
-- @field #string version
ARTY.version="1.2.0"
ARTY.version="1.3.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -801,6 +802,9 @@ function ARTY:New(group, alias)
else
self.ismobile=false
end
-- Set track time interval.
self.dtTrack=0.2
-- Set speed to 0.7 of maximum.
self.Speed=self.SpeedMax * 0.7
@ -1497,6 +1501,15 @@ function ARTY:SetStatusInterval(interval)
return self
end
--- Set time interval for weapon tracking.
-- @param #ARTY self
-- @param #number interval Time interval in seconds. Default 0.2 seconds.
-- @return self
function ARTY:SetTrackInterval(interval)
self.dtTrack=interval or 0.2
return self
end
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
-- @param #ARTY self
-- @param #number waittime Time in seconds. Default 300 seconds.
@ -2129,6 +2142,95 @@ end
-- Event Handling
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Function called during tracking of weapon.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #ARTY self ARTY object.
-- @param #ARTY.Target target Target of the weapon.
function ARTY._FuncTrack(weapon, self, target)
-- Coordinate and distance to target.
local _coord=weapon.coordinate
local _dist=_coord:Get2DDistance(target.coord)
local _destroyweapon=false
-- Debug
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
if target.weapontype==ARTY.WeaponType.IlluminationShells then
-- Check if within distace.
if _dist<target.radius then
-- Get random coordinate within certain radius of the target.
local _cr=target.coord:GetRandomCoordinateInRadius(target.radius)
-- Get random altitude over target.
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
-- Adjust explosion height of coordinate.
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
-- Create illumination flare.
_ci:IlluminationBomb(self.illuPower)
-- Destroy actual shell.
_destroyweapon=true
end
elseif target.weapontype==ARTY.WeaponType.SmokeShells then
if _dist<target.radius then
-- Get random coordinate within a certain radius.
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Fire smoke at this coordinate.
_cr:Smoke(self.smokeColor)
-- Destroy actual shell.
_destroyweapon=true
end
end
if _destroyweapon then
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
-- Destroy weapon and stop timer.
weapon:Destroy()
-- No more tracking.
weapon.tracking=false
end
end
--- Function called after impact of weapon.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #ARTY self ARTY object.
-- @param #ARTY.Target target Target of the weapon.
function ARTY._FuncImpact(weapon, self, target)
-- Debug info.
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
-- Get impact coordinate.
local _impactcoord=weapon:GetImpactCoordinate()
-- Create a "nuclear" explosion and blast at the impact point.
if target.weapontype==ARTY.WeaponType.TacticalNukes then
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
--SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
self:ScheduleOnce(1.0, ARTY._NuclearBlast, self, _impactcoord)
end
end
--- Eventhandler for shot event.
-- @param #ARTY self
-- @param Core.Event#EVENTDATA EventData
@ -2147,12 +2249,20 @@ function ARTY:OnEventShot(EventData)
self:T3(self.lid.."EVENT SHOT: Weapon name = ".._weaponName)
local group = EventData.IniGroup --Wrapper.Group#GROUP
env.info("FF 100")
if group and group:IsAlive() then
env.info("FF 200")
if EventData.IniGroupName == self.groupname then
env.info("FF 300")
if self.currentTarget then
env.info("FF 400")
-- Increase number of shots fired by this group on this target.
self.Nshots=self.Nshots+1
@ -2162,128 +2272,32 @@ function ARTY:OnEventShot(EventData)
self:T(self.lid..text)
MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug)
-- Last known position of the weapon fired.
local _lastpos={x=0, y=0, z=0}
--- Track the position of the weapon if it is supposed to model a tac nuke, illumination or smoke shell.
-- @param #table _weapon
local function _TrackWeapon(_data)
-- When the pcall status returns false the weapon has hit.
local _weaponalive,_currpos = pcall(
function()
return _data.weapon:getPoint()
end)
-- Debug
self:T3(self.lid..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_weaponalive)))
-- Destroy weapon before impact.
local _destroyweapon=false
if _weaponalive then
-- Update last position.
_lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z}
-- Coordinate and distance to target.
local _coord=COORDINATE:NewFromVec3(_lastpos)
local _dist=_coord:Get2DDistance(_data.target.coord)
-- Debug
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
if _data.target.weapontype==ARTY.WeaponType.IlluminationShells then
-- Check if within distace.
if _dist<_data.target.radius then
-- Get random coordinate within certain radius of the target.
local _cr=_data.target.coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Get random altitude over target.
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
-- Adjust explosion height of coordinate.
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
-- Create illumination flare.
_ci:IlluminationBomb(self.illuPower)
-- Destroy actual shell.
_destroyweapon=true
end
elseif _data.target.weapontype==ARTY.WeaponType.SmokeShells then
if _dist<_data.target.radius then
-- Get random coordinate within a certain radius.
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Fire smoke at this coordinate.
_cr:Smoke(self.smokeColor)
-- Destroy actual shell.
_destroyweapon=true
end
end
if _destroyweapon then
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
-- Destroy weapon and stop timer.
_data.weapon:destroy()
return nil
else
-- TODO: Make dt input parameter.
local dt=0.02
self:T3(self.lid..string.format("ARTY %s tracking weapon again in %.3f seconds", self.groupname, dt))
-- Check again in 0.05 seconds.
return timer.getTime() + dt
end
else
-- Get impact coordinate.
local _impactcoord=COORDINATE:NewFromVec3(_lastpos)
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
-- Create a "nuclear" explosion and blast at the impact point.
if _data.target.weapontype==ARTY.WeaponType.TacticalNukes then
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
end
-- Stop timer.
return nil
end
end
-- Start track the shell if we want to model a tactical nuke.
local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0
local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
if _tracknuke or _trackillu or _tracksmoke then
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
local _peter={}
_peter.weapon=EventData.weapon
_peter.target=UTILS.DeepCopy(self.currentTarget)
timer.scheduleFunction(_TrackWeapon, _peter, timer.getTime() + 2.0)
-- Debug info.
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
-- Create a weapon object.
local weapon=WEAPON:New(EventData.weapon)
-- Set time step for tracking.
weapon:SetTimeStepTrack(self.dtTrack)
-- Copy target. We need a copy because it might already be overwritten with the next target during flight of weapon.
local target=UTILS.DeepCopy(self.currentTarget)
-- Set callback functions.
weapon:SetFuncTrack(ARTY._FuncTrack, self, target)
weapon:SetFuncImpact(ARTY._FuncImpact, self, target)
-- Start tracking in 2 sec (arty ammo should fly a bit).
weapon:StartTrack(2)
end
-- Get current ammo.
@ -2842,6 +2856,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
end
for _,targetname in pairs(notpossible) do
self:E(self.lid..string.format("%s: Removing target %s because requested weapon is not possible with this type of unit.", self.groupname, targetname))
env.info("FF 1000",showMessageBox)
self:RemoveTarget(targetname)
end
@ -3931,9 +3946,10 @@ function ARTY:GetAmmo(display)
return nammo, nshells, nrockets, nmissiles
end
for _,unit in pairs(units) do
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
if unit then
-- Output.
local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName())

View File

@ -26,6 +26,7 @@
--- FOX class.
-- @type FOX
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file.
-- @field #table menuadded Table of groups the menu was added for.
@ -124,6 +125,7 @@
-- @field #FOX
FOX = {
ClassName = "FOX",
verbose = 0,
Debug = false,
lid = nil,
menuadded = {},
@ -185,6 +187,8 @@ FOX = {
-- @field #string targetName Name of the target unit or "unknown".
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
-- @field Core.Point#COORDINATE missileCoord Missile coordinate during tracking.
-- @field Wrapper.Weapon#WEAPON Weapon Weapon object.
--- Main radio menu on group level.
-- @field #table MenuF10 Root menu table on group level.
@ -196,7 +200,7 @@ FOX.MenuF10Root=nil
--- FOX class version.
-- @field #string version
FOX.version="0.7.0"
FOX.version="0.8.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@ -500,6 +504,7 @@ function FOX:SetDisableF10Menu()
return self
end
--- Enable F10 menu for all players.
-- @param #FOX self
-- @return #FOX self
@ -510,6 +515,15 @@ function FOX:SetEnableF10Menu()
return self
end
--- Set verbosity level.
-- @param #FOX self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #FOX self
function FOX:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set default player setting for missile destruction.
-- @param #FOX self
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
@ -605,7 +619,9 @@ function FOX:onafterStatus(From, Event, To)
local clock=UTILS.SecondsToClock(time)
-- Status.
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
if self.verbose>=1 then
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
end
-- Check missile status.
self:_CheckMissileStatus()
@ -713,7 +729,9 @@ function FOX:_CheckMissileStatus()
if #self.missiles==0 then
text=text.." none"
end
self:I(self.lid..text)
if self.verbose>=2 then
self:I(self.lid..text)
end
-- Remove inactive missiles.
for i=#self.missiles,1,-1 do
@ -743,7 +761,7 @@ function FOX:_IsProtected(targetunit)
if targetgroup then
local targetname=targetgroup:GetName()
for _,_group in pairs(self.protectedset:GetSetObjects()) do
for _,_group in pairs(self.protectedset:GetSet()) do
local group=_group --Wrapper.Group#GROUP
if group then
@ -763,21 +781,24 @@ function FOX:_IsProtected(targetunit)
end
--- Missle launch event.
-- @param #FOX self
--- Function called from weapon tracking.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #FOX self FOX object.
-- @param #FOX.MissileData missile Fired missile
function FOX:_FuncTrack(weapon, missile)
function FOX._FuncTrack(weapon, self, missile)
-- Missile coordinate.
missileCoord=COORDINATE:NewFromVec3(_lastBombPos)
local missileCoord= missile.missileCoord:UpdateFromVec3(weapon.vec3) --COORDINATE:NewFromVec3(_lastBombPos)
-- Missile velocity in m/s.
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
local missileVelocity=weapon:GetSpeed() --UTILS.VecNorm(_ordnance:getVelocity())
-- Update missile target if necessary.
self:GetMissileTarget(missile)
-- Target unit of the missile.
local target=nil --Wrapper.Unit#UNIT
if missile.targetUnit then
-----------------------------------
@ -868,13 +889,13 @@ function FOX:_FuncTrack(weapon, missile)
if unit:GetName()~=missile.shooterName then
-- Player position.
local playerCoord=unit:GetCoordinate()
-- Distance.
local dist=missileCoord:Get3DDistance(playerCoord)
local playerVec3=unit:GetVec3()
-- Distance.
local dist=missileCoord:Get3DDistance(playerVec3)
-- Distance from shooter to player.
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
local Dshooter2player=missile.shotCoord:Get3DDistance(playerVec3)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
@ -898,23 +919,22 @@ function FOX:_FuncTrack(weapon, missile)
if target then
-- Target coordinate.
local targetCoord=target:GetCoordinate()
local targetVec3=target:GetVec3() --target:GetCoordinate()
-- Distance from missile to target.
local distance=missileCoord:Get3DDistance(targetCoord)
local distance=missileCoord:Get3DDistance(targetVec3)
-- Distance missile to shooter.
local distShooter=nil
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate())
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetVec3())
end
-- Debug output.
if self.Debug then
local bearing=targetCoord:HeadingTo(missileCoord)
local eta=distance/missileVelocity
if self.Debug then
local bearing=missileCoord:HeadingTo(targetVec3)
local eta=distance/missileVelocity
-- Debug distance check.
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
end
@ -928,12 +948,12 @@ function FOX:_FuncTrack(weapon, missile)
end
-- If missile is 150 m from target ==> destroy missile if in safe zone.
if destroymissile and self:_CheckCoordSafe(targetCoord) then
if destroymissile and self:_CheckCoordSafe(targetVec3) then
-- Destroy missile.
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
_ordnance:destroy()
weapon:Destroy()
-- Missile is not active any more.
missile.active=false
@ -941,7 +961,6 @@ function FOX:_FuncTrack(weapon, missile)
-- Debug smoke.
if self.Debug then
missileCoord:SmokeRed()
targetCoord:SmokeGreen()
end
-- Create event.
@ -962,9 +981,9 @@ function FOX:_FuncTrack(weapon, missile)
-- Increase dead counter.
missile.targetPlayer.dead=missile.targetPlayer.dead+1
end
-- Terminate timer.
return nil
-- We could disable the tracking here but then the impact function would not be called.
--weapon.tracking=false
else
@ -987,30 +1006,30 @@ function FOX:_FuncTrack(weapon, missile)
dt=self.dt00 --0.01
end
-- Check again in dt seconds.
return timer.getTime()+dt
-- Set time step.
weapon:SetTimeStepTrack(dt)
end
else
-- Destroy missile.
-- No current target.
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
return timer.getTime()+0.1
weapon:SetTimeStepTrack(0.1)
end
end
--- Callback function on impact or destroy otherwise.
-- @param #FOX self
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #FOX.MissileData missile Fired missile
function FOX:_FuncImpact(weapon, missile)
-- @param #FOX self FOX object.
-- @param #FOX.MissileData missile Fired missile.
function FOX._FuncImpact(weapon, self, missile)
if target then
if missile.targetPlayer then
-- Get human player.
local player=self:_GetPlayerFromUnit(target)
local player=missile.targetPlayer
-- Check for player and distance < 10 km.
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
@ -1028,7 +1047,7 @@ function FOX:_FuncImpact(weapon, missile)
--Terminate the timer.
self:T(FOX.lid..string.format("Terminating missile track timer."))
return nil
weapon.tracking=false
end
@ -1090,18 +1109,17 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
end
end
local weapon=WEAPON:New(missile.weapon)
-- Set callback function for tracking.
missile.Weapon:SetFuncTrack(FOX._FuncTrack, self, missile)
weapon:SetFuncTrack(FuncTrack)
-- Set callback function for impact.
missile.Weapon:SetFuncImpact(FOX._FuncImpact, self, missile)
weapon:SetFuncImpact(FuncImpact)
-- Weapon is not yet "alife" just yet. Start timer with a little delay.
self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds."))
--timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
weapon:StartTrack(0.0001)
missile.Weapon:StartTrack(0.0001)
end
@ -1232,30 +1250,29 @@ end
-- @param Core.Event#EVENTDATA EventData
function FOX:OnEventShot(EventData)
self:T2({eventshot=EventData})
-- Nil checks.
if EventData.Weapon==nil or EventData.IniDCSUnit==nil or EventData.weapon==nil then
return
end
if EventData.Weapon==nil then
return
end
if EventData.IniDCSUnit==nil then
return
end
-- Create a weapon object.
local weapon=WEAPON:New(EventData.weapon)
-- Weapon data.
local _weapon = EventData.WeaponName
local _weapon = weapon:GetTypeName()
local _target = EventData.Weapon:getTarget()
local _targetName = "unknown"
local _targetUnit = nil --Wrapper.Unit#UNIT
-- Weapon descriptor.
local desc=EventData.Weapon:getDesc()
local desc=weapon.desc
self:T2({desc=desc})
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
local weaponcategory=desc.category
-- Missile category: 1=AAM, 2=SAM, 6=OTHER
local missilecategory=desc.missileCategory
-- Missile range.
local missilerange=nil
if missilecategory then
missilerange=desc.rangeMaxAltMax
@ -1265,8 +1282,8 @@ function FOX:OnEventShot(EventData)
self:T2(FOX.lid.."EVENT SHOT: FOX")
self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName)))
self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName)))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(_weapon)))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weaponcategory)))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(weapon:GetTypeName())))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weapon:GetCategory())))
self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory)))
self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange)))
@ -1278,7 +1295,7 @@ function FOX:OnEventShot(EventData)
end
-- Track missiles of type AAM=1, SAM=2 or OTHER=6
local _track = weaponcategory==1 and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
local _track = weapon:IsMissile() and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
-- Only track missiles
if _track then
@ -1287,6 +1304,7 @@ function FOX:OnEventShot(EventData)
missile.active=true
missile.weapon=EventData.weapon
missile.Weapon=weapon
missile.missileType=_weapon
missile.missileRange=missilerange
missile.missileName=EventData.weapon:getName()
@ -1299,6 +1317,7 @@ function FOX:OnEventShot(EventData)
missile.fuseDist=desc.fuseDist
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
missile.targetOrig=missile.targetName
missile.missileCoord=COORDINATE:New(0,0,0)
-- Set missile target name, unit and player.
self:GetMissileTarget(missile)
@ -1617,7 +1636,7 @@ end
--- Check if a coordinate lies within a safe training zone.
-- @param #FOX self
-- @param Core.Point#COORDINATE coord Coordinate to check.
-- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec3.
-- @return #boolean True if safe.
function FOX:_CheckCoordSafe(coord)
@ -1629,7 +1648,9 @@ function FOX:_CheckCoordSafe(coord)
-- Loop over all zones.
for _,_zone in pairs(self.safezones) do
local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsCoordinateInZone(coord)
local Vec2={x=coord.x, y=coord.z}
local inzone=zone:IsVec2InZone(Vec2)
--local inzone=zone:IsCoordinateInZone(coord)
if inzone then
return true
end
@ -1640,7 +1661,7 @@ end
--- Check if a coordinate lies within a launch zone.
-- @param #FOX self
-- @param Core.Point#COORDINATE coord Coordinate to check.
-- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec2.
-- @return #boolean True if in launch zone.
function FOX:_CheckCoordLaunch(coord)
@ -1652,7 +1673,9 @@ function FOX:_CheckCoordLaunch(coord)
-- Loop over all zones.
for _,_zone in pairs(self.launchzones) do
local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsCoordinateInZone(coord)
local Vec2={x=coord.x, y=coord.z}
local inzone=zone:IsVec2InZone(Vec2)
--local inzone=zone:IsCoordinateInZone(coord)
if inzone then
return true
end

View File

@ -1833,13 +1833,13 @@ function RANGE:OnEventHit( EventData )
end
--- Function called on impact of a tracked weapon.
-- @param #RANGE self
-- @param Wrapper.Weapon#WEAPON weapon The weapon object.
-- @param #RANGE self RANGE object.
-- @param #RANGE.PlayerData playerData Player data table.
-- @param #number attackHdg Attack heading.
-- @param #number attackAlt Attack altitude.
-- @param #number attackVel Attack velocity.
function RANGE:_OnImpact(weapon, playerData, attackHdg, attackAlt, attackVel)
function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackVel)
-- Get closet target to last position.
local _closetTarget = nil -- #RANGE.BombTarget
@ -1964,10 +1964,6 @@ function RANGE:_OnImpact(weapon, playerData, attackHdg, attackAlt, attackVel)
self:T( self.lid .. "Weapon impacted outside range zone." )
end
-- Terminate the timer
self:T( self.lid .. string.format( "Range %s, player %s: Terminating bomb track timer.", self.rangename, _playername ) )
return nil
end
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
@ -1993,7 +1989,7 @@ function RANGE:OnEventShot( EventData )
-- Get player unit and name.
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
-- Set this to larger value than the threshold.
-- Distance Player-to-Range. Set this to larger value than the threshold.
local dPR = self.BombtrackThreshold * 2
-- Distance player to range.
@ -2027,14 +2023,6 @@ function RANGE:OnEventShot( EventData )
end
--- Check spawn queue and spawn aircraft if necessary.
-- @param #RANGE self
-- @param #string PlayerName Name of player.
-- @return #RANGE.BombResult
function RANGE:_GetBombResults(PlayerName)
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -1197,6 +1197,20 @@ function UTILS.HdgDiff(h1, h2)
return math.abs(delta)
end
--- Returns the heading from one vec3 to another vec3.
-- @param DCS#Vec3 a From vec3.
-- @param DCS#Vec3 b To vec3.
-- @return #number Heading in degrees.
function UTILS.HdgTo(a, b)
local dz=b.z-a.z
local dx=b.x-a.x
local heading=math.deg(math.atan2(dz, dx))
if heading < 0 then
heading = 360 + heading
end
return heading
end
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.

View File

@ -167,7 +167,7 @@ end
--- Get the DCS unit object.
-- @param #UNIT self
-- @return DCS#Unit
-- @return DCS#Unit The DCS unit object.
function UNIT:GetDCSObject()
local DCSUnit = Unit.getByName( self.UnitName )

View File

@ -3,10 +3,12 @@
-- ## Main Features:
--
-- * Convenient access to DCS API functions
-- * Track weapon and get impact position
-- * Track weapon and get impact position
-- * Get launcher and target of weapon
-- * Define callback function when weapon impacts
-- * Destroy weapon before impact
-- * Define callback function when tracking weapon
-- * Mark impact points on F10 map
-- * Put coloured smoke on impact points
--
-- ===
--
@ -31,7 +33,8 @@
-- @field DCS#Weapon weapon The DCS weapon object.
-- @field #string name Name of the weapon object.
-- @field #string typeName Type name of the weapon.
-- @field #number category Weapon category 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB (Weapon.Category.X).
-- @field #number category Weapon category 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB, 4=TORPEDO (Weapon.Category.X).
-- @field #number categoryMissile Missile category 0=AAM, 1=SAM, 2=BM, 3=ANTI_SHIP, 4=CRUISE, 5=OTHER (Weapon.MissileCategory.X).
-- @field #number coalition Coalition ID.
-- @field #number country Country ID.
-- @field DCS#Desc desc Descriptor table.
@ -45,11 +48,18 @@
-- @field #table trackArg Optional arguments for the track callback function.
-- @field DCS#Vec3 vec3 Last known 3D position vector of the tracked weapon.
-- @field DCS#Position3 pos3 Last known 3D position and direction vector of the tracked weapon.
-- @field Core.Point#COORDINATE coordinate Coordinate object of the weapon. Can be used in other classes.
-- @field DCS#Vec3 impactVec3 Impact 3D vector.
-- @field Core.Point#COORDINATE impactCoord Impact coordinate.
-- @field #number trackScheduleID Tracking scheduler ID. Can be used to remove/destroy the scheduler function.
-- @field #boolean tracking If `true`, scheduler will keep tracking. Otherwise, function will return nil and stop tracking.
-- @field #boolean markImpact If `true`, the impact point is marked on the F10 map. Requires tracking to be started.
-- @field #boolean impactMark If `true`, the impact point is marked on the F10 map. Requires tracking to be started.
-- @field #boolean impactSmoke If `true`, the impact point is marked by smoke. Requires tracking to be started.
-- @field #number impactSmokeColor Colour of impact point smoke.
-- @field #boolean impactDestroy If `true`, destroy weapon before impact. Requires tracking to be started and sufficiently small time step.
-- @field #number impactDestroyDist Distance in meters to the estimated impact point. If smaller, then weapon is destroyed.
-- @field #number distIP Distance in meters for the intercept point estimation.
-- @field Wrapper.Unit#UNIT target Last known target.
-- @extends Wrapper.Positionable#POSITIONABLE
--- *In the long run, the sharpest weapon of all is a kind and gentle spirit.* -- Anne Frank
@ -68,15 +78,21 @@
--
-- # Tracking
--
-- The status of the weapon can be tracked with the @{#WEAPON.StartTrack}() function. This function will try to determin the position of the weapon in (normally) relatively
-- small time steps. The time step can be set via the @{#WEAPON.SetTimeStepTrack} function and is by default set to 0.01 secons.
-- The status of the weapon can be tracked with the @{#WEAPON.StartTrack} function. This function will try to determin the position of the weapon in (normally) relatively
-- small time steps. The time step can be set via the @{#WEAPON.SetTimeStepTrack} function and is by default set to 0.01 seconds.
--
-- Once the position cannot be retrieved any more, the weapon has impacted (or was destroyed otherwise) and the last known position is safed as the impact point.
-- The impact point can be accessed with the @{#WEAPON.GetImpactVec3} or @{#WEAPON.GetImpactCoordinate} functions.
--
-- ## Impact Point Marking
--
-- You can mark the impact point on the F10 map with @{#WEAPON.SetMarkImpact}.
--
-- You can also trigger coloured smoke at the impact point via @{#WEAPON.SetSmokeImpact}.
--
-- ## Callback functions
--
-- It is possible to define functions that are called during the tracking of the weapon and upon impact.
-- It is possible to define functions that are called during the tracking of the weapon and upon impact, which help you to customize further actions.
--
-- ### Callback on Impact
--
@ -86,6 +102,38 @@
--
-- The function called each time the weapon status is tracked can be set with @{#WEAPON.SetFuncTrack}
--
-- # Target
--
-- If the weapon has a specific target, you can get it with the @{#WEAPON.GetTarget} function. Note that the object, which is returned can vary. Normally, it is a UNIT
-- but it could also be a STATIC object.
--
-- Also note that the weapon does not always have a target, it can loose a target and re-aquire it and the target might change to another unit.
--
-- You can get the target name with the @{#WEAPON.GetTargetName} function.
--
-- The distance to the target is returned by the @{#WEAPON.GetTargetDistance} function.
--
-- # Category
--
-- The category (bomb, rocket, missile, shell, torpedo) of the weapon can be retrieved with the @{#WEAPON.GetCategory} function.
--
-- You can check if the weapon is a
--
-- * bomb with @{#WEAPON.IsBomb}
-- * rocket with @{#WEAPON.IsRocket}
-- * missile with @{#WEAPON.IsMissile}
-- * shell with @{#WEAPON.IsShell}
-- * torpedo with @{#WEAPON.IsTorpedo}
--
-- # Parameters
--
-- You can get various parameters of the weapon, *e.g.*
--
-- * position: @{#WEAPON.GetVec3}, @{#WEAPON.GetVec2 }, @{#WEAPON.GetCoordinate}
-- * speed: @{#WEAPON.GetSpeed}
-- * coalition: @{#WEAPON.GetCoalition}
-- * country: @{#WEAPON.GetCountry}
--
-- # Dependencies
--
-- This class is used (at least) in the MOOSE classes:
@ -103,7 +151,7 @@ WEAPON = {
--- WEAPON class version.
-- @field #string version
WEAPON.version="0.0.1"
WEAPON.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -149,7 +197,7 @@ function WEAPON:New(WeaponObject)
end
-- Get type name.
self.typeName=WeaponObject:getTypeName()
self.typeName=WeaponObject:getTypeName() or "Unknown Type"
-- Get name of object. Usually a number like "1234567".
self.name=WeaponObject:getName()
@ -170,18 +218,23 @@ function WEAPON:New(WeaponObject)
self.launcherUnit=UNIT:Find(self.launcher)
end
-- Init the coordinate of the weapon from that of the launcher.
self.coordinate=COORDINATE:NewFromVec3(self.launcher:getPoint())
-- Set log ID.
self.lid=string.format("[%s] %s | ", self.typeName, self.name)
-- Set default parameters
self:SetTimeStepTrack()
self:SetDistanceInterceptPoint()
-- Debug info.
local text=string.format("FF Weapon: Name=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
self.name, self.typeName, self.category, self.coalition, self.country, self.launcherName)
env.info(text)
local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
self.version, self.name, self.typeName, self.category, self.coalition, self.country, self.launcherName)
self:T(self.lid..text)
self:I(self.desc)
-- Descriptors.
self:T2(self.desc)
return self
end
@ -208,6 +261,19 @@ function WEAPON:SetTimeStepTrack(TimeStep)
return self
end
--- Set distance of intercept point for estimated impact point.
-- If the weapon cannot be tracked any more, the intercept point from its last known position and direction is used to get
-- a better approximation of the impact point. Can be useful when using longer time steps in the tracking and still achieve
-- a good result on the impact point.
-- It uses the DCS function [getIP](https://wiki.hoggitworld.com/view/DCS_func_getIP).
-- @param #WEAPON self
-- @param #number Distance Distance in meters. Default is 50 m. Set to 0 to deactivate.
-- @return #WEAPON self
function WEAPON:SetDistanceInterceptPoint(Distance)
self.distIP=Distance or 50
return self
end
--- Mark impact point on the F10 map. This requires that the tracking has been started.
-- @param #WEAPON self
-- @param #boolean Switch If `true` or nil, impact is marked.
@ -215,14 +281,33 @@ end
function WEAPON:SetMarkImpact(Switch)
if Switch==false then
self.markImpact=false
self.impactMark=false
else
self.markImpact=true
self.impactMark=true
end
return self
end
--- Put smoke on impact point. This requires that the tracking has been started.
-- @param #WEAPON self
-- @param #boolean Switch If `true` or nil, impact is smoked.
-- @param #number SmokeColor Color of smoke. Default is `SMOKECOLOR.Red`.
-- @return #WEAPON self
function WEAPON:SetSmokeImpact(Switch, SmokeColor)
if Switch==false then
self.impactSmoke=false
else
self.impactSmoke=true
end
self.impactSmokeColor=SmokeColor or SMOKECOLOR.Red
return self
end
--- Set callback function when weapon is tracked and still alive. The first argument will be the WEAPON object.
-- Note that this can be called many times per second. So be careful for performance reasons.
-- @param #WEAPON self
@ -284,20 +369,19 @@ function WEAPON:GetTarget()
-- Get object category.
local category=object:getCategory()
-- Get object name.
--Target name
local name=object:getName()
-- Debug info.
self:I(self.lid..string.format("Got Target Object %s, category=%d", name, category))
self:T(self.lid..string.format("Got Target Object %s, category=%d", object:getName(), category))
if category==Object.Category.UNIT then
target=UNIT:Find(object)
target=UNIT:FindByName(name)
elseif category==Object.Category.STATIC then
target=STATIC:Find(object)
target=STATIC:FindByName(name, false)
elseif category==Object.Category.SCENERY then
self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
@ -308,10 +392,57 @@ function WEAPON:GetTarget()
end
end
return target
end
--- Get the distance to the current target the weapon is guiding to.
-- @param #WEAPON self
-- @param #function ConversionFunction (Optional) Conversion function from meters to desired unit, *e.g.* `UTILS.MpsToKmph`.
-- @return #number Distance from weapon to target in meters.
function WEAPON:GetTargetDistance(ConversionFunction)
-- Get the target of the weapon.
local target=self:GetTarget() --Wrapper.Unit#UNIT
local distance=nil
if target then
-- Current position of target.
local tv3=target:GetVec3()
-- Current position of weapon.
local wv3=self:GetVec3()
if tv3 and wv3 then
distance=UTILS.VecDist3D(tv3, wv3)
if ConversionFunction then
distance=ConversionFunction(distance)
end
end
end
return distance
end
--- Get name the current target the weapon is guiding to.
-- @param #WEAPON self
-- @return #string Name of the target or "None" if no target.
function WEAPON:GetTargetName()
-- Get the target of the weapon.
local target=self:GetTarget() --Wrapper.Unit#UNIT
local name="None"
if target then
name=target:GetName()
end
return name
end
--- Get velocity vector of weapon.
-- @param #WEAPON self
@ -326,8 +457,9 @@ end
--- Get speed of weapon.
-- @param #WEAPON self
-- @param #function ConversionFunction (Optional) Conversion function from m/s to desired unit, *e.g.* `UTILS.MpsToKmph`.
-- @return #number Speed in meters per second.
function WEAPON:GetSpeed()
function WEAPON:GetSpeed(ConversionFunction)
local speed=nil
@ -337,6 +469,10 @@ function WEAPON:GetSpeed()
speed=UTILS.VecNorm(v)
if ConversionFunction then
speed=ConversionFunction(speed)
end
end
return speed
@ -344,7 +480,7 @@ end
--- Get the current 3D position vector.
-- @param #WEAPON self
-- @return DCS#Vec3
-- @return DCS#Vec3 Current position vector in 3D.
function WEAPON:GetVec3()
local vec3=nil
@ -355,6 +491,24 @@ function WEAPON:GetVec3()
return vec3
end
--- Get the current 2D position vector.
-- @param #WEAPON self
-- @return DCS#Vec2 Current position vector in 2D.
function WEAPON:GetVec2()
local vec3=self:GetVec3()
if vec3 then
local vec2={x=vec3.x, y=vec3.z}
return vec2
end
return nil
end
--- Get type name.
-- @param #WEAPON self
-- @return #string The type name.
@ -469,6 +623,7 @@ function WEAPON:Destroy(Delay)
else
if self.weapon then
self:T(self.lid.."Destroying Weapon NOW!")
self:StopTrack()
self.weapon:destroy()
end
end
@ -481,10 +636,11 @@ end
-- the (approximate) impact point. Of course, the smaller the time step, the better the position can be determined. However, this can hit the performance as many
-- calculations per second need to be carried out.
-- @param #WEAPON self
-- @param #number Delay Delay in seconds before the tracking starts. Default 0.001 sec. This is also the minimum.
-- @param #number Delay Delay in seconds before the tracking starts. Default 0.001 sec.
-- @return #WEAPON self
function WEAPON:StartTrack(Delay)
-- Set delay before start.
Delay=math.max(Delay or 0.001, 0.001)
-- Debug info.
@ -530,7 +686,9 @@ end
function WEAPON:_TrackWeapon(time)
-- Debug info.
self:T3(self.lid..string.format("Tracking at T=%.5f", time))
if self.verbose>=20 then
self:I(self.lid..string.format("Tracking at T=%.5f", time))
end
-- Protected call to get the weapon position. If the position cannot be determined any more, the weapon has impacted and status is nil.
local status, pos3= pcall(
@ -550,33 +708,41 @@ function WEAPON:_TrackWeapon(time)
self.pos3 = pos3
-- Update last known vec3.
self.vec3 = self.pos3.p
self.vec3 = UTILS.DeepCopy(self.pos3.p)
-- Update coordinate.
self.coordinate:UpdateFromVec3(self.vec3)
-- Keep on tracking by returning the next time below.
self.tracking=true
-- Callback function.
if self.trackFunc then
self.trackFunc(self, unpack(self.trackArg or {}))
self.trackFunc(self, unpack(self.trackArg))
end
-- Verbose output.
if self.verbose>=5 then
-- Get vec2 of current position.
local vec2={x=self.vec3.x, y=self.vec3.z}
-- Land hight.
local height=land.getHeight(vec2)
-- Current height above ground level.
local agl=self.vec3.y-height
-- Estimated IP (if any)
local ip=self:_GetIP(100)
local ip=self:_GetIP(self.distIP)
-- Distance between positon and estimated impact.
local d=0
if ip then
d=UTILS.VecDist3D(self.vec3, ip)
end
-- Output.
self:I(self.lid..string.format("T=%.3f: Height=%.3f m AGL=%.3f m, dIP=%.3f", time, height, agl, d))
end
@ -588,10 +754,12 @@ function WEAPON:_TrackWeapon(time)
---------------------------
-- Get intercept point from position (p) and direction (x) in 50 meters.
local ip = self:_GetIP(50)
local ip = self:_GetIP(self.distIP)
if ip then
env.info("FF Got intercept point!")
if self.verbose>=10 and ip then
-- Output.
self:I(self.lid.."Got intercept point!")
-- Coordinate of the impact point.
local coord=COORDINATE:NewFromVec3(ip)
@ -603,7 +771,8 @@ function WEAPON:_TrackWeapon(time)
-- Distance to last known pos.
local d=UTILS.VecDist3D(ip, self.vec3)
env.info(string.format("FF d(ip, vec3)=%.3f meters", d))
-- Output.
self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters", d))
end
@ -614,10 +783,15 @@ function WEAPON:_TrackWeapon(time)
self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
-- Mark impact point on F10 map.
if self.markImpact then
if self.impactMark then
self.impactCoord:MarkToAll(string.format("Impact point of weapon %s\ntype=%s\nlauncher=%s", self.name, self.typeName, self.launcherName))
end
-- Smoke on impact point.
if self.impactSmoke then
self.impactCoord:Smoke(self.impactSmokeColor)
end
-- Call callback function.
if self.impactFunc then
self.impactFunc(self, unpack(self.impactArg or {}))
@ -642,12 +816,20 @@ end
--- Compute estimated intercept/impact point (IP) based on last known position and direction.
-- @param #WEAPON self
-- @param #number Distance Distance in meters. Default 20 m.
-- @param #number Distance Distance in meters. Default 50 m.
-- @return DCS#Vec3 Estimated intercept/impact point. Can also return `nil`, if no IP can be determined.
function WEAPON:_GetIP(Distance)
-- Get intercept point from position (p) and direction (x) in 20 meters.
local ip = land.getIP(self.pos3.p, self.pos3.x, Distance or 20) --DCS#Vec3
Distance=Distance or 50
local ip=nil --DCS#Vec3
if Distance>0 and self.pos3 then
-- Get intercept point from position (p) and direction (x) in 20 meters.
ip = land.getIP(self.pos3.p, self.pos3.x, Distance or 20) --DCS#Vec3
end
return ip
end