mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
#Fixes from dev
This commit is contained in:
parent
9cfed56847
commit
e95c1ad3aa
@ -305,8 +305,8 @@ EVENTS = {
|
||||
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
|
||||
-- @field #string PlaceName The name of the airbase.
|
||||
--
|
||||
-- @field #table weapon The weapon used during the event.
|
||||
-- @field #table Weapon
|
||||
-- @field DCS#Weapon weapon The weapon used during the event.
|
||||
-- @field DCS#Weapon Weapon The weapon used during the event.
|
||||
-- @field #string WeaponName Name of the weapon.
|
||||
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
|
||||
--
|
||||
|
||||
@ -1160,12 +1160,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
|
||||
|
||||
|
||||
|
||||
@ -555,7 +555,7 @@ function ZONE_BASE:GetZoneMaybe()
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
||||
--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #string PropertyName The name of a the TriggerZone Property to be retrieved.
|
||||
-- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent.
|
||||
@ -569,7 +569,7 @@ function ZONE_BASE:GetProperty(PropertyName)
|
||||
return self.Properties[PropertyName]
|
||||
end
|
||||
|
||||
-- Returns the zone Properties table.
|
||||
--- Returns the zone Properties table.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return #table The Key:Value table of TriggerZone properties of the zone.
|
||||
function ZONE_BASE:GetAllProperties()
|
||||
@ -927,7 +927,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
||||
local ZoneCoord = self:GetCoordinate()
|
||||
local ZoneRadius = self:GetRadius()
|
||||
|
||||
self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||
--self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
@ -2084,13 +2084,10 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the smallest circular zone encompassing all points points of the polygon zone.
|
||||
--- Get the smallest radius encompassing all points of the polygon zone.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone.
|
||||
-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered.
|
||||
-- @return #ZONE_RADIUS The circular zone.
|
||||
function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
||||
-- @return #number Radius of the zone in meters.
|
||||
function ZONE_POLYGON_BASE:GetRadius()
|
||||
|
||||
local center=self:GetVec2()
|
||||
|
||||
@ -2107,6 +2104,20 @@ function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
||||
|
||||
end
|
||||
|
||||
return radius
|
||||
end
|
||||
|
||||
--- Get the smallest circular zone encompassing all points of the polygon zone.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone.
|
||||
-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered.
|
||||
-- @return #ZONE_RADIUS The circular zone.
|
||||
function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
||||
|
||||
local center=self:GetVec2()
|
||||
|
||||
local radius=self:GetRadius()
|
||||
|
||||
local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone)
|
||||
|
||||
return zone
|
||||
@ -2913,7 +2924,7 @@ do -- ZONE_ELASTIC
|
||||
|
||||
--- Add a set of groups. Positions of the group will be considered as polygon vertices when contructing the convex hull.
|
||||
-- @param #ZONE_ELASTIC self
|
||||
-- @param Core.Set#SET_GROUP SetGroup Set of groups.
|
||||
-- @param Core.Set#SET_GROUP GroupSet Set of groups.
|
||||
-- @return #ZONE_ELASTIC self
|
||||
function ZONE_ELASTIC:AddSetGroup(GroupSet)
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -802,6 +803,9 @@ function ARTY:New(group, alias)
|
||||
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
|
||||
@ -2162,128 +2264,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))
|
||||
-- Debug info.
|
||||
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)
|
||||
-- Create a weapon object.
|
||||
local weapon=WEAPON:New(EventData.weapon)
|
||||
|
||||
timer.scheduleFunction(_TrackWeapon, _peter, timer.getTime() + 2.0)
|
||||
-- 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.
|
||||
@ -3931,9 +3937,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())
|
||||
|
||||
@ -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 = {},
|
||||
@ -168,7 +170,7 @@ FOX = {
|
||||
|
||||
--- Missile data table.
|
||||
-- @type FOX.MissileData
|
||||
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit.
|
||||
-- @field DCS#Weapon weapon Missile weapon object.
|
||||
-- @field #boolean active If true the missile is active.
|
||||
-- @field #string missileType Type of missile.
|
||||
-- @field #string missileName Name of missile.
|
||||
@ -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.6.1"
|
||||
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
|
||||
@ -762,6 +780,277 @@ function FOX:_IsProtected(targetunit)
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- 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, self, missile)
|
||||
|
||||
-- Missile coordinate.
|
||||
local missileCoord= missile.missileCoord:UpdateFromVec3(weapon.vec3) --COORDINATE:NewFromVec3(_lastBombPos)
|
||||
|
||||
-- Missile velocity in m/s.
|
||||
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
|
||||
|
||||
-----------------------------------
|
||||
-- Missile has a specific target --
|
||||
-----------------------------------
|
||||
|
||||
if missile.targetPlayer then
|
||||
-- Target is a player.
|
||||
if missile.targetPlayer.destroy==true then
|
||||
target=missile.targetUnit
|
||||
end
|
||||
else
|
||||
-- Check if unit is protected.
|
||||
if self:_IsProtected(missile.targetUnit) then
|
||||
target=missile.targetUnit
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
------------------------------------
|
||||
-- Missile has NO specific target --
|
||||
------------------------------------
|
||||
|
||||
-- TODO: This might cause a problem with wingman. Even if the shooter itself is excluded from the check, it's wingmen are not.
|
||||
-- That would trigger the distance check right after missile launch if things to wrong.
|
||||
--
|
||||
-- Possible solutions:
|
||||
-- * Time check: enable this check after X seconds after missile was fired. What is X?
|
||||
-- * Coalition check. But would not work in training situations where blue on blue is valid!
|
||||
-- * At least enable it for surface-to-air missiles.
|
||||
|
||||
local function _GetTarget(_unit)
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
-- Player position.
|
||||
local playerCoord=unit:GetCoordinate()
|
||||
|
||||
-- Distance.
|
||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||
|
||||
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||
if dist<=self.explosiondist then
|
||||
return unit
|
||||
end
|
||||
end
|
||||
|
||||
-- Distance to closest player.
|
||||
local mindist=nil
|
||||
|
||||
-- Loop over players.
|
||||
for _,_player in pairs(self.players) do
|
||||
local player=_player --#FOX.PlayerData
|
||||
|
||||
-- Check that player was not the one who launched the missile.
|
||||
if player.unitname~=missile.shooterName then
|
||||
|
||||
-- Player position.
|
||||
local playerCoord=player.unit:GetCoordinate()
|
||||
|
||||
-- Distance.
|
||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||
|
||||
-- Distance from shooter to player.
|
||||
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
||||
|
||||
-- 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
|
||||
mindist=dist
|
||||
target=player.unit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.protectedset then
|
||||
|
||||
-- Distance to closest protected unit.
|
||||
mindist=nil
|
||||
|
||||
for _,_group in pairs(self.protectedset:GetSet()) do
|
||||
local group=_group --Wrapper.Group#GROUP
|
||||
for _,_unit in pairs(group:GetUnits()) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
if unit and unit:IsAlive() then
|
||||
|
||||
-- Check that player was not the one who launched the missile.
|
||||
if unit:GetName()~=missile.shooterName then
|
||||
|
||||
-- Player position.
|
||||
local playerVec3=unit:GetVec3()
|
||||
|
||||
-- Distance.
|
||||
local dist=missileCoord:Get3DDistance(playerVec3)
|
||||
|
||||
-- Distance from shooter to player.
|
||||
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
|
||||
mindist=dist
|
||||
target=unit
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target then
|
||||
self:T(self.lid..string.format("Missile %s with NO explicit target got closest unit to missile as target %s. Dist=%s m", missile.missileType, target:GetName(), tostring(mindist)))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Check if missile has a valid target.
|
||||
if target then
|
||||
|
||||
-- Target coordinate.
|
||||
local targetVec3=target:GetVec3() --target:GetCoordinate()
|
||||
|
||||
-- Distance from missile to target.
|
||||
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:GetVec3())
|
||||
end
|
||||
|
||||
|
||||
-- Debug output.
|
||||
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
|
||||
|
||||
-- Distroy missile if it's getting too close.
|
||||
local destroymissile=distance<=self.explosiondist
|
||||
|
||||
-- Check BIG missiles.
|
||||
if self.explosiondist2 and distance<=self.explosiondist2 and not destroymissile then
|
||||
destroymissile=missile.explosive>=self.bigmissilemass
|
||||
end
|
||||
|
||||
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
||||
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))
|
||||
weapon:Destroy()
|
||||
|
||||
-- Missile is not active any more.
|
||||
missile.active=false
|
||||
|
||||
-- Debug smoke.
|
||||
if self.Debug then
|
||||
missileCoord:SmokeRed()
|
||||
end
|
||||
|
||||
-- Create event.
|
||||
self:MissileDestroyed(missile)
|
||||
|
||||
-- Little explosion for the visual effect.
|
||||
if self.explosionpower>0 and distance>50 and (distShooter==nil or (distShooter and distShooter>50)) then
|
||||
missileCoord:Explosion(self.explosionpower)
|
||||
end
|
||||
|
||||
-- Target was a player.
|
||||
if missile.targetPlayer then
|
||||
|
||||
-- Message to target.
|
||||
local text=string.format("Destroying missile. %s", self:_DeadText())
|
||||
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
||||
|
||||
-- Increase dead counter.
|
||||
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
||||
end
|
||||
|
||||
-- We could disable the tracking here but then the impact function would not be called.
|
||||
--weapon.tracking=false
|
||||
|
||||
else
|
||||
|
||||
-- Time step.
|
||||
local dt=1.0
|
||||
if distance>50000 then
|
||||
-- > 50 km
|
||||
dt=self.dt50 --=5.0
|
||||
elseif distance>10000 then
|
||||
-- 10-50 km
|
||||
dt=self.dt10 --=1.0
|
||||
elseif distance>5000 then
|
||||
-- 5-10 km
|
||||
dt=self.dt05 --0.5
|
||||
elseif distance>1000 then
|
||||
-- 1-5 km
|
||||
dt=self.dt01 --0.1
|
||||
else
|
||||
-- < 1 km
|
||||
dt=self.dt00 --0.01
|
||||
end
|
||||
|
||||
-- Set time step.
|
||||
weapon:SetTimeStepTrack(dt)
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- 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))
|
||||
weapon:SetTimeStepTrack(0.1)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Callback function on impact or destroy otherwise.
|
||||
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||
-- @param #FOX self FOX object.
|
||||
-- @param #FOX.MissileData missile Fired missile.
|
||||
function FOX._FuncImpact(weapon, self, missile)
|
||||
|
||||
if missile.targetPlayer then
|
||||
|
||||
-- Get human player.
|
||||
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
|
||||
local text=string.format("Missile defeated. Well done, %s!", player.name)
|
||||
MESSAGE:New(text, 10):ToClient(player.client)
|
||||
|
||||
-- Increase defeated counter.
|
||||
player.defeated=player.defeated+1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Missile is not active any more.
|
||||
missile.active=false
|
||||
|
||||
--Terminate the timer.
|
||||
self:T(FOX.lid..string.format("Terminating missile track timer."))
|
||||
weapon.tracking=false
|
||||
|
||||
end
|
||||
|
||||
--- Missle launch event.
|
||||
-- @param #FOX self
|
||||
-- @param #string From From state.
|
||||
@ -820,302 +1109,17 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
end
|
||||
end
|
||||
|
||||
-- Init missile position.
|
||||
local _lastBombPos = {x=0,y=0,z=0}
|
||||
-- Set callback function for tracking.
|
||||
missile.Weapon:SetFuncTrack(FOX._FuncTrack, self, missile)
|
||||
|
||||
-- Missile coordinate.
|
||||
local missileCoord = nil --Core.Point#COORDINATE
|
||||
-- Set callback function for impact.
|
||||
missile.Weapon:SetFuncImpact(FOX._FuncImpact, self, missile)
|
||||
|
||||
-- Target unit of the missile.
|
||||
local target=nil --Wrapper.Unit#UNIT
|
||||
|
||||
--- Function monitoring the position of a bomb until impact.
|
||||
local function trackMissile(_ordnance)
|
||||
|
||||
-- When the pcall returns a failure the weapon has hit.
|
||||
local _status,_bombPos = pcall(
|
||||
function()
|
||||
return _ordnance:getPoint()
|
||||
end)
|
||||
|
||||
-- Check if status is not nil. If so, we have a valid point.
|
||||
if _status then
|
||||
|
||||
----------------------------------------------
|
||||
-- Still in the air. Remember this position --
|
||||
----------------------------------------------
|
||||
|
||||
-- Missile position.
|
||||
_lastBombPos = {x=_bombPos.x, y=_bombPos.y, z=_bombPos.z}
|
||||
|
||||
-- Missile coordinate.
|
||||
missileCoord=COORDINATE:NewFromVec3(_lastBombPos)
|
||||
|
||||
-- Missile velocity in m/s.
|
||||
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
|
||||
|
||||
-- Update missile target if necessary.
|
||||
self:GetMissileTarget(missile)
|
||||
|
||||
if missile.targetUnit then
|
||||
|
||||
-----------------------------------
|
||||
-- Missile has a specific target --
|
||||
-----------------------------------
|
||||
|
||||
if missile.targetPlayer then
|
||||
-- Target is a player.
|
||||
if missile.targetPlayer.destroy==true then
|
||||
target=missile.targetUnit
|
||||
end
|
||||
else
|
||||
-- Check if unit is protected.
|
||||
if self:_IsProtected(missile.targetUnit) then
|
||||
target=missile.targetUnit
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
------------------------------------
|
||||
-- Missile has NO specific target --
|
||||
------------------------------------
|
||||
|
||||
-- TODO: This might cause a problem with wingman. Even if the shooter itself is excluded from the check, it's wingmen are not.
|
||||
-- That would trigger the distance check right after missile launch if things to wrong.
|
||||
--
|
||||
-- Possible solutions:
|
||||
-- * Time check: enable this check after X seconds after missile was fired. What is X?
|
||||
-- * Coalition check. But would not work in training situations where blue on blue is valid!
|
||||
-- * At least enable it for surface-to-air missiles.
|
||||
|
||||
local function _GetTarget(_unit)
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
-- Player position.
|
||||
local playerCoord=unit:GetCoordinate()
|
||||
|
||||
-- Distance.
|
||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||
|
||||
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||
if dist<=self.explosiondist then
|
||||
return unit
|
||||
end
|
||||
end
|
||||
|
||||
-- Distance to closest player.
|
||||
local mindist=nil
|
||||
|
||||
-- Loop over players.
|
||||
for _,_player in pairs(self.players) do
|
||||
local player=_player --#FOX.PlayerData
|
||||
|
||||
-- Check that player was not the one who launched the missile.
|
||||
if player.unitname~=missile.shooterName then
|
||||
|
||||
-- Player position.
|
||||
local playerCoord=player.unit:GetCoordinate()
|
||||
|
||||
-- Distance.
|
||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||
|
||||
-- Distance from shooter to player.
|
||||
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
||||
|
||||
-- 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
|
||||
mindist=dist
|
||||
target=player.unit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.protectedset then
|
||||
|
||||
-- Distance to closest protected unit.
|
||||
mindist=nil
|
||||
|
||||
for _,_group in pairs(self.protectedset:GetSet()) do
|
||||
local group=_group --Wrapper.Group#GROUP
|
||||
for _,_unit in pairs(group:GetUnits()) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
if unit and unit:IsAlive() then
|
||||
|
||||
-- Check that player was not the one who launched the missile.
|
||||
if unit:GetName()~=missile.shooterName then
|
||||
|
||||
-- Player position.
|
||||
local playerCoord=unit:GetCoordinate()
|
||||
|
||||
-- Distance.
|
||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||
|
||||
-- Distance from shooter to player.
|
||||
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
||||
|
||||
-- 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
|
||||
mindist=dist
|
||||
target=unit
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target then
|
||||
self:T(self.lid..string.format("Missile %s with NO explicit target got closest unit to missile as target %s. Dist=%s m", missile.missileType, target:GetName(), tostring(mindist)))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Check if missile has a valid target.
|
||||
if target then
|
||||
|
||||
-- Target coordinate.
|
||||
local targetCoord=target:GetCoordinate()
|
||||
|
||||
-- Distance from missile to target.
|
||||
local distance=missileCoord:Get3DDistance(targetCoord)
|
||||
|
||||
-- Distance missile to shooter.
|
||||
local distShooter=nil
|
||||
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
|
||||
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate())
|
||||
end
|
||||
|
||||
|
||||
-- Debug output.
|
||||
if self.Debug then
|
||||
local bearing=targetCoord:HeadingTo(missileCoord)
|
||||
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
|
||||
|
||||
-- Distroy missile if it's getting too close.
|
||||
local destroymissile=distance<=self.explosiondist
|
||||
|
||||
-- Check BIG missiles.
|
||||
if self.explosiondist2 and distance<=self.explosiondist2 and not destroymissile then
|
||||
destroymissile=missile.explosive>=self.bigmissilemass
|
||||
end
|
||||
|
||||
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
||||
if destroymissile and self:_CheckCoordSafe(targetCoord) 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()
|
||||
|
||||
-- Missile is not active any more.
|
||||
missile.active=false
|
||||
|
||||
-- Debug smoke.
|
||||
if self.Debug then
|
||||
missileCoord:SmokeRed()
|
||||
targetCoord:SmokeGreen()
|
||||
end
|
||||
|
||||
-- Create event.
|
||||
self:MissileDestroyed(missile)
|
||||
|
||||
-- Little explosion for the visual effect.
|
||||
if self.explosionpower>0 and distance>50 and (distShooter==nil or (distShooter and distShooter>50)) then
|
||||
missileCoord:Explosion(self.explosionpower)
|
||||
end
|
||||
|
||||
-- Target was a player.
|
||||
if missile.targetPlayer then
|
||||
|
||||
-- Message to target.
|
||||
local text=string.format("Destroying missile. %s", self:_DeadText())
|
||||
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
||||
|
||||
-- Increase dead counter.
|
||||
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
||||
end
|
||||
|
||||
-- Terminate timer.
|
||||
return nil
|
||||
|
||||
else
|
||||
|
||||
-- Time step.
|
||||
local dt=1.0
|
||||
if distance>50000 then
|
||||
-- > 50 km
|
||||
dt=self.dt50 --=5.0
|
||||
elseif distance>10000 then
|
||||
-- 10-50 km
|
||||
dt=self.dt10 --=1.0
|
||||
elseif distance>5000 then
|
||||
-- 5-10 km
|
||||
dt=self.dt05 --0.5
|
||||
elseif distance>1000 then
|
||||
-- 1-5 km
|
||||
dt=self.dt01 --0.1
|
||||
else
|
||||
-- < 1 km
|
||||
dt=self.dt00 --0.01
|
||||
end
|
||||
|
||||
-- Check again in dt seconds.
|
||||
return timer.getTime()+dt
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Destroy missile.
|
||||
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
|
||||
|
||||
-- No target ==> terminate timer.
|
||||
--return nil
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-------------------------------------
|
||||
-- Missile does not exist any more --
|
||||
-------------------------------------
|
||||
|
||||
if target then
|
||||
|
||||
-- Get human player.
|
||||
local player=self:_GetPlayerFromUnit(target)
|
||||
|
||||
-- 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
|
||||
local text=string.format("Missile defeated. Well done, %s!", player.name)
|
||||
MESSAGE:New(text, 10):ToClient(player.client)
|
||||
|
||||
-- Increase defeated counter.
|
||||
player.defeated=player.defeated+1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Missile is not active any more.
|
||||
missile.active=false
|
||||
|
||||
--Terminate the timer.
|
||||
self:T(FOX.lid..string.format("Terminating missile track timer."))
|
||||
return nil
|
||||
|
||||
end -- _status check
|
||||
|
||||
end -- end function trackBomb
|
||||
|
||||
-- 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)
|
||||
--timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
|
||||
missile.Weapon:StartTrack(0.0001)
|
||||
|
||||
end
|
||||
|
||||
@ -1247,29 +1251,28 @@ end
|
||||
function FOX:OnEventShot(EventData)
|
||||
self:T2({eventshot=EventData})
|
||||
|
||||
if EventData.Weapon==nil then
|
||||
return
|
||||
end
|
||||
if EventData.IniDCSUnit==nil then
|
||||
-- Nil checks.
|
||||
if EventData.Weapon==nil or EventData.IniDCSUnit==nil or EventData.weapon==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
|
||||
@ -1279,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)))
|
||||
|
||||
@ -1292,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
|
||||
@ -1301,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()
|
||||
@ -1313,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)
|
||||
@ -1631,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)
|
||||
|
||||
@ -1643,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
|
||||
@ -1654,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)
|
||||
|
||||
@ -1666,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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -44,6 +44,7 @@ SOCKET = {
|
||||
}
|
||||
|
||||
--- Data type. This is the keyword the socket listener uses.
|
||||
-- @type SOCKET.DataType
|
||||
-- @field #string TEXT Plain text.
|
||||
-- @field #string BOMBRESULT Range bombing.
|
||||
-- @field #string STRAFERESULT Range strafeing result.
|
||||
|
||||
@ -1222,6 +1222,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.
|
||||
@ -2026,9 +2040,9 @@ function UTILS.GenerateUHFrequencies()
|
||||
local _start = 220000000
|
||||
|
||||
while _start < 399000000 do
|
||||
if _start ~= 243000000 then
|
||||
table.insert(FreeUHFFrequencies, _start)
|
||||
end
|
||||
if _start ~= 243000000 then
|
||||
table.insert(FreeUHFFrequencies, _start)
|
||||
end
|
||||
_start = _start + 500000
|
||||
end
|
||||
|
||||
|
||||
@ -703,11 +703,11 @@ function POSITIONABLE:IsSubmarine()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitDescriptor = DCSUnit:getDesc()
|
||||
if UnitDescriptor.attributes["Submarines"] == true then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
if UnitDescriptor.attributes["Submarines"] == true then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
self:E( { "Cannot check IsSubmarine", Positionable = self, Alive = self:IsAlive() } )
|
||||
@ -1768,6 +1768,7 @@ do -- Cargo
|
||||
["tt_DSHK"] = 6,
|
||||
["HL_KORD"] = 6,
|
||||
["HL_DSHK"] = 6,
|
||||
["CCKW_353"] = 16, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers
|
||||
}
|
||||
|
||||
-- Assuming that each passenger weighs 95 kg on average.
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions: **Applevangelist**
|
||||
-- ### Contributions: **Applevangelist**, **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@ -12,12 +12,12 @@
|
||||
-- @image Wrapper_Scenery.JPG
|
||||
|
||||
|
||||
|
||||
--- @type SCENERY
|
||||
-- @field #string ClassName
|
||||
-- @field #string SceneryName
|
||||
-- @field #DCS.Object SceneryObject
|
||||
-- @field #number Life0
|
||||
--- SCENERY Class
|
||||
-- @type SCENERY
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #string SceneryName Name of the scenery object.
|
||||
-- @field DCS#Object SceneryObject DCS scenery object.
|
||||
-- @field #number Life0 Initial life points.
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
|
||||
@ -37,12 +37,16 @@ SCENERY = {
|
||||
--- Register scenery object as POSITIONABLE.
|
||||
--@param #SCENERY self
|
||||
--@param #string SceneryName Scenery name.
|
||||
--@param #DCS.Object SceneryObject DCS scenery object.
|
||||
--@param DCS#Object SceneryObject DCS scenery object.
|
||||
--@return #SCENERY Scenery object.
|
||||
function SCENERY:Register( SceneryName, SceneryObject )
|
||||
|
||||
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
|
||||
|
||||
self.SceneryName = SceneryName
|
||||
|
||||
self.SceneryObject = SceneryObject
|
||||
|
||||
if self.SceneryObject then
|
||||
self.Life0 = self.SceneryObject:getLife()
|
||||
else
|
||||
@ -53,7 +57,7 @@ end
|
||||
|
||||
--- Obtain DCS Object from the SCENERY Object.
|
||||
--@param #SCENERY self
|
||||
--@return #DCS.Object DCS scenery object.
|
||||
--@return DCS#Object DCS scenery object.
|
||||
function SCENERY:GetDCSObject()
|
||||
return self.SceneryObject
|
||||
end
|
||||
@ -69,7 +73,7 @@ function SCENERY:GetLife()
|
||||
return life
|
||||
end
|
||||
|
||||
--- Get current initial life points from the SCENERY Object.
|
||||
--- Get initial life points of the SCENERY Object.
|
||||
--@param #SCENERY self
|
||||
--@return #number life
|
||||
function SCENERY:GetLife0()
|
||||
@ -90,7 +94,7 @@ function SCENERY:IsDead()
|
||||
return self:GetLife() < 1 and true or false
|
||||
end
|
||||
|
||||
--- Get the threat level of a SCENERY object. Always 0.
|
||||
--- Get the threat level of a SCENERY object. Always 0 as scenery does not pose a threat to anyone.
|
||||
--@param #SCENERY self
|
||||
--@return #number Threat level 0.
|
||||
--@return #string "Scenery".
|
||||
|
||||
@ -89,9 +89,9 @@
|
||||
--
|
||||
-- @field #UNIT
|
||||
UNIT = {
|
||||
ClassName="UNIT",
|
||||
UnitName=nil,
|
||||
GroupName=nil,
|
||||
ClassName="UNIT",
|
||||
UnitName=nil,
|
||||
GroupName=nil,
|
||||
}
|
||||
|
||||
|
||||
@ -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 )
|
||||
@ -325,14 +325,19 @@ function UNIT:IsAlive()
|
||||
local DCSUnit = self:GetDCSObject() -- DCS#Unit
|
||||
|
||||
if DCSUnit then
|
||||
local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive()
|
||||
local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() -- and DCSUnit:getLife() > 1
|
||||
return UnitIsAlive
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns if the Unit is dead.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean `true` if Unit is dead, else false or nil if the unit does not exist
|
||||
function UNIT:IsDead()
|
||||
return not self:IsAlive()
|
||||
end
|
||||
|
||||
--- Returns the Unit's callsign - the localized string.
|
||||
-- @param #UNIT self
|
||||
@ -626,7 +631,7 @@ function UNIT:IsFuelSupply()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Returns the unit's group if it exist and nil otherwise.
|
||||
--- Returns the unit's group if it exists and nil otherwise.
|
||||
-- @param Wrapper.Unit#UNIT self
|
||||
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
||||
function UNIT:GetGroup()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user