mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
commit
d3d60757c1
@ -540,7 +540,7 @@ do -- COORDINATE
|
||||
local gotscenery=false
|
||||
|
||||
local function EvaluateZone(ZoneObject)
|
||||
|
||||
BASE:T({ZoneObject})
|
||||
if ZoneObject then
|
||||
|
||||
-- Get category of scanned object.
|
||||
@ -1322,6 +1322,14 @@ do -- COORDINATE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set altitude to be at land height (i.e. on the ground!)
|
||||
-- @param #COORDINATE self
|
||||
function COORDINATE:SetAtLandheight()
|
||||
local alt=self:GetLandHeight()
|
||||
self.y=alt
|
||||
return self
|
||||
end
|
||||
|
||||
--- Build an air type route point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
@ -1947,7 +1955,6 @@ do -- COORDINATE
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string name (Optional) Name of the fire to stop it, if not using the same COORDINATE object.
|
||||
function COORDINATE:StopBigSmokeAndFire( name )
|
||||
self:F2( { name = name } )
|
||||
name = name or self.firename
|
||||
trigger.action.effectSmokeStop( name )
|
||||
end
|
||||
|
||||
@ -873,8 +873,10 @@ end
|
||||
function SCORING:OnEventBirth( Event )
|
||||
|
||||
if Event.IniUnit then
|
||||
Event.IniUnit.ThreatLevel, Event.IniUnit.ThreatType = Event.IniUnit:GetThreatLevel()
|
||||
if Event.IniObjectCategory == 1 then
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
Event.IniUnit.BirthTime = timer.getTime()
|
||||
if PlayerName then
|
||||
self:_AddPlayerFromUnit( Event.IniUnit )
|
||||
self:SetScoringMenu( Event.IniGroup )
|
||||
@ -1005,7 +1007,18 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
||||
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||
if PlayerHit.UNIT.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
-- if this fails for some reason, set a good default value
|
||||
if PlayerHit.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel = 1
|
||||
PlayerHit.ThreatType = "Unknown"
|
||||
end
|
||||
else
|
||||
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||
end
|
||||
|
||||
-- Only grant hit scores if there was more than one second between the last hit.
|
||||
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
||||
@ -1021,19 +1034,20 @@ function SCORING:_EventOnHit( Event )
|
||||
|
||||
if InitCoalition then -- A coalition object was hit.
|
||||
if InitCoalition == TargetCoalition then
|
||||
Player.Penalty = Player.Penalty + 10
|
||||
PlayerHit.Penalty = PlayerHit.Penalty + 10
|
||||
local Penalty = 10
|
||||
Player.Penalty = Player.Penalty + Penalty
|
||||
PlayerHit.Penalty = PlayerHit.Penalty + Penalty
|
||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
|
||||
|
||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
"Penalty: -" .. Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Update )
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
"Penalty: -" .. Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Update )
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -1104,7 +1118,18 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
||||
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||
if PlayerHit.UNIT.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
-- if this fails for some reason, set a good default value
|
||||
if PlayerHit.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel = 1
|
||||
PlayerHit.ThreatType = "Unknown"
|
||||
end
|
||||
else
|
||||
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||
end
|
||||
|
||||
-- Only grant hit scores if there was more than one second between the last hit.
|
||||
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
||||
@ -1115,14 +1140,15 @@ function SCORING:_EventOnHit( Event )
|
||||
if InitCoalition then -- A coalition object was hit, probably a static.
|
||||
if InitCoalition == TargetCoalition then
|
||||
-- TODO: Penalty according scale
|
||||
Player.Penalty = Player.Penalty + 10 --* self.ScaleDestroyPenalty
|
||||
PlayerHit.Penalty = PlayerHit.Penalty + 10 --* self.ScaleDestroyPenalty
|
||||
local Penalty = 10
|
||||
Player.Penalty = Player.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||
PlayerHit.Penalty = PlayerHit.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
|
||||
|
||||
MESSAGE
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||
"Penalty: -" .. Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
@ -1133,7 +1159,7 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.Score = PlayerHit.Score + 1
|
||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty,
|
||||
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Update )
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -1211,7 +1237,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
local Destroyed = false
|
||||
|
||||
-- What is the player destroying?
|
||||
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered???
|
||||
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
|
||||
|
||||
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
|
||||
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
|
||||
@ -1240,13 +1266,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
|
||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
@ -1268,13 +1294,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
|
||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
||||
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
||||
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information )
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
|
||||
@ -715,7 +715,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
if UnitHit.ClassName ~= "SCENERY" then
|
||||
if UnitHit and UnitHit.ClassName ~= "SCENERY" then
|
||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Positionable.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Unit.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Weapon.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
|
||||
|
||||
@ -524,7 +524,7 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType, Payloads)
|
||||
if a and b then -- I had the case that a or b were nil even though the self.payloads table was looking okay. Very strange! Seems to be solved by pre-selecting valid payloads.
|
||||
local performanceA=self:GetPayloadPeformance(a, MissionType)
|
||||
local performanceB=self:GetPayloadPeformance(b, MissionType)
|
||||
return (performanceA>performanceB) or (performanceA==performanceB and a.unlimited==true) or (performanceA==performanceB and a.unlimited==true and b.unlimited==true and a.navail>b.navail)
|
||||
return (performanceA>performanceB) or (performanceA==performanceB and a.unlimited==true and b.unlimited~=true) or (performanceA==performanceB and a.unlimited==true and b.unlimited==true and a.navail>b.navail)
|
||||
elseif not a then
|
||||
self:I(self.lid..string.format("FF ERROR in sortpayloads: a is nil"))
|
||||
return false
|
||||
|
||||
@ -713,7 +713,8 @@ do
|
||||
-- my_ctld.placeCratesAhead = false -- place crates straight ahead of the helicopter, in a random way. If true, crates are more neatly sorted.
|
||||
-- my_ctld.nobuildinloadzones = true -- forbid players to build stuff in LOAD zones if set to `true`
|
||||
-- my_ctld.movecratesbeforebuild = true -- crates must be moved once before they can be build. Set to false for direct builds.
|
||||
-- my_ctld.surfacetypes = {land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.RUNWAY,land.SurfaceType.SHALLOW_WATER} -- surfaces for loading back objects
|
||||
-- my_ctld.surfacetypes = {land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.RUNWAY,land.SurfaceType.SHALLOW_WATER} -- surfaces for loading back objects.
|
||||
-- my_ctld.nobuildmenu = false -- if set to true effectively enforces to have engineers build/repair stuff for you.
|
||||
--
|
||||
-- ## 2.1 User functions
|
||||
--
|
||||
@ -732,6 +733,7 @@ do
|
||||
-- ["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12, cargoweightlimit = 400},
|
||||
-- ["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8, length = 15, cargoweightlimit = 700},
|
||||
-- ["Mi-8MT"] = {type="Mi-8MT", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15, cargoweightlimit = 3000},
|
||||
-- ["Mi-8MTV2"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15, cargoweightlimit = 3000},
|
||||
-- ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15, cargoweightlimit = 0},
|
||||
-- ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700},
|
||||
-- ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700},
|
||||
@ -1180,7 +1182,7 @@ CTLD.UnitTypes = {
|
||||
["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12, cargoweightlimit = 400},
|
||||
["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8, length = 15, cargoweightlimit = 700},
|
||||
["Mi-8MTV2"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15, cargoweightlimit = 3000},
|
||||
["Mi-8MT"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15, cargoweightlimit = 3000},
|
||||
["Mi-8MT"] = {type="Mi-8MT", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15, cargoweightlimit = 3000},
|
||||
["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15, cargoweightlimit = 0},
|
||||
["Ka-50_3"] = {type="Ka-50_3", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15, cargoweightlimit = 0},
|
||||
["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700},
|
||||
@ -1194,7 +1196,7 @@ CTLD.UnitTypes = {
|
||||
|
||||
--- CTLD class version.
|
||||
-- @field #string version
|
||||
CTLD.version="1.0.27"
|
||||
CTLD.version="1.0.30"
|
||||
|
||||
--- Instantiate a new CTLD.
|
||||
-- @param #CTLD self
|
||||
@ -1306,6 +1308,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
||||
self.Engineers = 0 -- #number use as counter
|
||||
self.EngineersInField = {} -- #table holds #CTLD_ENGINEERING objects
|
||||
self.EngineerSearch = 2000 -- #number search distance for crates to build or repair
|
||||
self.nobuildmenu = false -- enfore engineer build only?
|
||||
|
||||
-- setup
|
||||
self.CrateDistance = 35 -- list/load crates in this radius
|
||||
@ -1773,6 +1776,22 @@ function CTLD:_FindTroopsCargoObject(Name)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- (Internal) Find a crates CTLD_CARGO object in stock
|
||||
-- @param #CTLD self
|
||||
-- @param #string Name of the object
|
||||
-- @return #CTLD_CARGO Cargo object, nil if it cannot be found
|
||||
function CTLD:_FindCratesCargoObject(Name)
|
||||
self:T(self.lid .. " _FindCratesCargoObject")
|
||||
local cargo = nil
|
||||
for _,_cargo in pairs(self.Cargo_Crates)do
|
||||
local cargo = _cargo -- #CTLD_CARGO
|
||||
if cargo.Name == Name then
|
||||
return cargo
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- (User) Pre-load troops into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot!
|
||||
-- @param #CTLD self
|
||||
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
||||
@ -1798,6 +1817,84 @@ function CTLD:PreloadTroops(Unit,Troopname)
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Pre-load crates into a helo. Do not use standalone!
|
||||
-- @param #CTLD self
|
||||
-- @param Wrapper.Group#GROUP Group The group to load into, can be handed as Wrapper.Client#CLIENT object
|
||||
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
||||
-- @param #CTLD_CARGO Cargo The Cargo crate object to load
|
||||
-- @param #number NumberOfCrates (Optional) Number of crates to be loaded. Default - all necessary to build this object. Might overload the helo!
|
||||
-- @return #CTLD self
|
||||
function CTLD:_PreloadCrates(Group, Unit, Cargo, NumberOfCrates)
|
||||
-- load crate into heli
|
||||
local group = Group -- Wrapper.Group#GROUP
|
||||
local unit = Unit -- Wrapper.Unit#UNIT
|
||||
local unitname = unit:GetName()
|
||||
-- see if this heli can load crates
|
||||
local unittype = unit:GetTypeName()
|
||||
local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities
|
||||
local cancrates = capabilities.crates -- #boolean
|
||||
local cratelimit = capabilities.cratelimit -- #number
|
||||
if not cancrates then
|
||||
self:_SendMessage("Sorry this chopper cannot carry crates!", 10, false, Group)
|
||||
return self
|
||||
else
|
||||
-- have we loaded stuff already?
|
||||
local numberonboard = 0
|
||||
local massonboard = 0
|
||||
local loaded = {}
|
||||
if self.Loaded_Cargo[unitname] then
|
||||
loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo
|
||||
numberonboard = loaded.Cratesloaded or 0
|
||||
massonboard = self:_GetUnitCargoMass(Unit)
|
||||
else
|
||||
loaded = {} -- #CTLD.LoadedCargo
|
||||
loaded.Troopsloaded = 0
|
||||
loaded.Cratesloaded = 0
|
||||
loaded.Cargo = {}
|
||||
end
|
||||
local crate = Cargo -- #CTLD_CARGO
|
||||
local numbercrates = NumberOfCrates or crate:GetCratesNeeded()
|
||||
for i=1,numbercrates do
|
||||
loaded.Cratesloaded = loaded.Cratesloaded + 1
|
||||
crate:SetHasMoved(true)
|
||||
crate:SetWasDropped(false)
|
||||
table.insert(loaded.Cargo, crate)
|
||||
crate.Positionable = nil
|
||||
self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()), 10, false, Group)
|
||||
--self:__CratesPickedUp(1, Group, Unit, crate)
|
||||
self.Loaded_Cargo[unitname] = loaded
|
||||
self:_UpdateUnitCargoMass(Unit)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- (User) Pre-load crates into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot!
|
||||
-- @param #CTLD self
|
||||
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
||||
-- @param #string Cratesname The name of the cargo to be loaded. Must be created prior in the CTLD setup!
|
||||
-- @param #number NumberOfCrates (Optional) Number of crates to be loaded. Default - all necessary to build this object. Might overload the helo!
|
||||
-- @return #CTLD self
|
||||
-- @usage
|
||||
-- local client = UNIT:FindByName("Helo-1-1")
|
||||
-- if client and client:IsAlive() then
|
||||
-- myctld:PreloadCrates(client,"Humvee")
|
||||
-- end
|
||||
function CTLD:PreloadCrates(Unit,Cratesname,NumberOfCrates)
|
||||
self:T(self.lid .. " PreloadCrates")
|
||||
local name = Cratesname or "Unknown"
|
||||
if Unit and Unit:IsAlive() then
|
||||
local cargo = self:_FindCratesCargoObject(name)
|
||||
local group = Unit:GetGroup()
|
||||
if cargo then
|
||||
self:_PreloadCrates(group,Unit,cargo,NumberOfCrates)
|
||||
else
|
||||
self:E(self.lid.." Crates preload - Cargo Object "..name.." not found!")
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Function to load troops into a heli.
|
||||
-- @param #CTLD self
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
@ -3323,6 +3420,12 @@ function CTLD:_RefreshF10Menus()
|
||||
self.subcats[entry.Subcategory] = entry.Subcategory
|
||||
end
|
||||
end
|
||||
for _id,_cargo in pairs(self.Cargo_Statics) do
|
||||
local entry = _cargo -- #CTLD_CARGO
|
||||
if not self.subcats[entry.Subcategory] then
|
||||
self.subcats[entry.Subcategory] = entry.Subcategory
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- build unit menus
|
||||
@ -3386,6 +3489,13 @@ function CTLD:_RefreshF10Menus()
|
||||
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
|
||||
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
|
||||
end
|
||||
for _,_entry in pairs(self.Cargo_Statics) do
|
||||
local entry = _entry -- #CTLD_CARGO
|
||||
local subcat = entry.Subcategory
|
||||
menucount = menucount + 1
|
||||
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
|
||||
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
|
||||
end
|
||||
else
|
||||
for _,_entry in pairs(self.Cargo_Crates) do
|
||||
local entry = _entry -- #CTLD_CARGO
|
||||
@ -3393,17 +3503,21 @@ function CTLD:_RefreshF10Menus()
|
||||
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
|
||||
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
|
||||
end
|
||||
end
|
||||
for _,_entry in pairs(self.Cargo_Statics) do
|
||||
local entry = _entry -- #CTLD_CARGO
|
||||
menucount = menucount + 1
|
||||
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
|
||||
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
|
||||
for _,_entry in pairs(self.Cargo_Statics) do
|
||||
local entry = _entry -- #CTLD_CARGO
|
||||
menucount = menucount + 1
|
||||
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
|
||||
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
|
||||
end
|
||||
end
|
||||
listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit)
|
||||
local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit)
|
||||
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit)
|
||||
local repairmenu = MENU_GROUP_COMMAND:New(_group,"Repair",topcrates, self._RepairCrates, self, _group, _unit):Refresh()
|
||||
if not self.nobuildmenu then
|
||||
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit)
|
||||
local repairmenu = MENU_GROUP_COMMAND:New(_group,"Repair",topcrates, self._RepairCrates, self, _group, _unit):Refresh()
|
||||
else
|
||||
unloadmenu:Refresh()
|
||||
end
|
||||
end
|
||||
if self:IsHercules(_unit) then
|
||||
local hoverpars = MENU_GROUP_COMMAND:New(_group,"Show flight parameters",topmenu, self._ShowFlightParams, self, _group, _unit):Refresh()
|
||||
@ -3488,13 +3602,14 @@ end
|
||||
-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1".
|
||||
-- @param #number Mass Mass in kg of each static in kg, e.g. 100.
|
||||
-- @param #number Stock Number of groups in stock. Nil for unlimited.
|
||||
function CTLD:AddStaticsCargo(Name,Mass,Stock)
|
||||
-- @param #string SubCategory Name of sub-category (optional).
|
||||
function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory)
|
||||
self:T(self.lid .. " AddStaticsCargo")
|
||||
self.CargoCounter = self.CargoCounter + 1
|
||||
local type = CTLD_CARGO.Enum.STATIC
|
||||
local template = STATIC:FindByName(Name,true):GetTypeName()
|
||||
-- Crates are not directly loadable
|
||||
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock)
|
||||
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock,SubCategory)
|
||||
table.insert(self.Cargo_Statics,cargo)
|
||||
return self
|
||||
end
|
||||
@ -5252,7 +5367,7 @@ CTLD_HERCULES = {
|
||||
ClassName = "CTLD_HERCULES",
|
||||
lid = "",
|
||||
Name = "",
|
||||
Version = "0.0.2",
|
||||
Version = "0.0.3",
|
||||
}
|
||||
|
||||
--- Define cargo types.
|
||||
@ -5570,8 +5685,8 @@ function CTLD_HERCULES:Cargo_SpawnObjects(Cargo_Drop_initiator,Cargo_Drop_Direct
|
||||
|
||||
if offload_cargo == true or ParatrooperGroupSpawn == true then
|
||||
if ParatrooperGroupSpawn == true then
|
||||
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 0)
|
||||
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 5)
|
||||
--self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 0)
|
||||
--self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 5)
|
||||
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 10)
|
||||
else
|
||||
self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country)
|
||||
|
||||
@ -104,7 +104,7 @@ PLAYERRECCE = {
|
||||
ClassName = "PLAYERRECCE",
|
||||
verbose = true,
|
||||
lid = nil,
|
||||
version = "0.0.16",
|
||||
version = "0.0.17",
|
||||
ViewZone = {},
|
||||
ViewZoneVisual = {},
|
||||
ViewZoneLaser = {},
|
||||
@ -149,7 +149,8 @@ PLAYERRECCE.LaserRelativePos = {
|
||||
["SA342Mistral"] = { x = 1.7, y = 1.2, z = 0 },
|
||||
["SA342Minigun"] = { x = 1.7, y = 1.2, z = 0 },
|
||||
["SA342L"] = { x = 1.7, y = 1.2, z = 0 },
|
||||
["Ka-50"] = { x = 6.1, y = -0.85 , z = 0 }
|
||||
["Ka-50"] = { x = 6.1, y = -0.85 , z = 0 },
|
||||
["Ka-50_3"] = { x = 6.1, y = -0.85 , z = 0 }
|
||||
}
|
||||
|
||||
---
|
||||
@ -161,6 +162,7 @@ PLAYERRECCE.MaxViewDistance = {
|
||||
["SA342Minigun"] = 8000,
|
||||
["SA342L"] = 8000,
|
||||
["Ka-50"] = 8000,
|
||||
["Ka-50_3"] = 8000,
|
||||
}
|
||||
|
||||
---
|
||||
@ -172,6 +174,7 @@ PLAYERRECCE.Cameraheight = {
|
||||
["SA342Minigun"] = 2.85,
|
||||
["SA342L"] = 2.85,
|
||||
["Ka-50"] = 0.5,
|
||||
["Ka-50_3"] = 0.5,
|
||||
}
|
||||
|
||||
---
|
||||
@ -183,6 +186,7 @@ PLAYERRECCE.CanLase = {
|
||||
["SA342Minigun"] = false, -- no optics
|
||||
["SA342L"] = true,
|
||||
["Ka-50"] = true,
|
||||
["Ka-50_3"] = true,
|
||||
}
|
||||
|
||||
---
|
||||
@ -338,10 +342,6 @@ function PLAYERRECCE:_GetClockDirection(unit, target)
|
||||
clock = UTILS.Round(clock,0)
|
||||
if clock > 12 then clock = clock-12 end
|
||||
end
|
||||
--if self.debug then
|
||||
--local text = string.format("Heading = %d, Angle = %d, Hours= %d, Clock = %d",_heading,Angle,hours,clock)
|
||||
--self:I(self.lid .. text)
|
||||
--end
|
||||
return clock
|
||||
end
|
||||
|
||||
@ -411,7 +411,7 @@ function PLAYERRECCE:_CameraOn(client,playername)
|
||||
if vivihorizontal < -0.7 or vivihorizontal > 0.7 then
|
||||
camera = false
|
||||
end
|
||||
elseif typename == "Ka-50" then
|
||||
elseif string.find(typename,"Ka-50") then
|
||||
camera = true
|
||||
end
|
||||
end
|
||||
@ -653,7 +653,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera,laser)
|
||||
angle=10
|
||||
-- Model nod and actual TV view don't compute
|
||||
maxview = self.MaxViewDistance[typename] or 5000
|
||||
elseif typename == "Ka-50" and camera then
|
||||
elseif string.find(typename,"Ka-50") and camera then
|
||||
heading = unit:GetHeading()
|
||||
nod,maxview,camon = 10,1000,true
|
||||
angle = 10
|
||||
|
||||
839
Moose Development/Moose/Wrapper/Weapon.lua
Normal file
839
Moose Development/Moose/Wrapper/Weapon.lua
Normal file
@ -0,0 +1,839 @@
|
||||
--- **Wrapper** - Weapon functions.
|
||||
--
|
||||
-- ## Main Features:
|
||||
--
|
||||
-- * Convenient access to DCS API functions
|
||||
-- * Track weapon and get impact position
|
||||
-- * Get launcher and target of weapon
|
||||
-- * Define callback function when weapon impacts
|
||||
-- * Define callback function when tracking weapon
|
||||
-- * Mark impact points on F10 map
|
||||
-- * Put coloured smoke on impact points
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Wrapper%20-%20Weapon).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Wrapper.Weapon
|
||||
-- @image Wrapper_Weapon.png
|
||||
|
||||
|
||||
--- WEAPON class.
|
||||
-- @type WEAPON
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity level.
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @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, 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.
|
||||
-- @field DCS#Unit launcher Launcher DCS unit.
|
||||
-- @field Wrapper.Unit#UNIT launcherUnit Launcher Unit.
|
||||
-- @field #string launcherName Name of launcher unit.
|
||||
-- @field #number dtTrack Time step in seconds for tracking scheduler.
|
||||
-- @field #function impactFunc Callback function for weapon impact.
|
||||
-- @field #table impactArg Optional arguments for the impact callback function.
|
||||
-- @field #function trackFunc Callback function when weapon is tracked and alive.
|
||||
-- @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 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
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The WEAPON Concept
|
||||
--
|
||||
-- The WEAPON class offers an easy-to-use wrapper interface to all DCS API functions.
|
||||
--
|
||||
-- Probably, the most striking highlight is that the position of the weapon can be tracked and its impact position can be determined, which is not
|
||||
-- possible with the native DCS scripting engine functions.
|
||||
--
|
||||
-- **Note** that this wrapper class is different from most others as weapon objects cannot be found with a DCS API function like `getByName()`.
|
||||
-- They can only be found in DCS events like the "Shot" event, where the weapon object is contained in the event data.
|
||||
--
|
||||
-- # 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 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, which help you to customize further actions.
|
||||
--
|
||||
-- ### Callback on Impact
|
||||
--
|
||||
-- The function called on impact can be set with @{#WEAPON.SetFuncImpact}
|
||||
--
|
||||
-- ### Callback when Tracking
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
-- * RANGE (to determine the impact points of bombs and missiles)
|
||||
-- * ARTY (to destroy and replace shells with smoke or illumination)
|
||||
-- * FOX (to destroy the missile before it hits the target)
|
||||
--
|
||||
-- @field #WEAPON
|
||||
WEAPON = {
|
||||
ClassName = "WEAPON",
|
||||
verbose = 0,
|
||||
}
|
||||
|
||||
|
||||
--- WEAPON class version.
|
||||
-- @field #string version
|
||||
WEAPON.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
-- TODO: Destroy before impact.
|
||||
-- TODO: Monitor target.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new WEAPON object from the DCS weapon object.
|
||||
-- @param #WEAPON self
|
||||
-- @param DCS#Weapon WeaponObject The DCS weapon object.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:New(WeaponObject)
|
||||
|
||||
-- Nil check on object.
|
||||
if WeaponObject==nil then
|
||||
env.error("ERROR: Weapon object does NOT exist")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, POSITIONABLE:New("Weapon")) -- #WEAPON
|
||||
|
||||
-- Set DCS weapon object.
|
||||
self.weapon=WeaponObject
|
||||
|
||||
-- Descriptors containing a lot of info.
|
||||
self.desc=WeaponObject:getDesc()
|
||||
|
||||
-- This gives the object category which is always Object.Category.WEAPON!
|
||||
--self.category=WeaponObject:getCategory()
|
||||
|
||||
-- Weapon category: 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB (Weapon.Category.X)
|
||||
self.category = self.desc.category
|
||||
|
||||
if self:IsMissile() and self.desc.missileCategory then
|
||||
self.categoryMissile=self.desc.missileCategory
|
||||
end
|
||||
|
||||
-- Get type name.
|
||||
self.typeName=WeaponObject:getTypeName() or "Unknown Type"
|
||||
|
||||
-- Get name of object. Usually a number like "1234567".
|
||||
self.name=WeaponObject:getName()
|
||||
|
||||
-- Get coaliton of weapon.
|
||||
self.coalition=WeaponObject:getCoalition()
|
||||
|
||||
-- Get country of weapon.
|
||||
self.country=WeaponObject:getCountry()
|
||||
|
||||
-- Get DCS unit of the launcher.
|
||||
self.launcher=WeaponObject:getLauncher()
|
||||
|
||||
-- Get launcher of weapon.
|
||||
self.launcherName="Unknown Launcher"
|
||||
if self.launcher then
|
||||
self.launcherName=self.launcher:getName()
|
||||
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("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)
|
||||
|
||||
-- Descriptors.
|
||||
self:T2(self.desc)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User API Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set verbosity level.
|
||||
-- @param #WEAPON self
|
||||
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:SetVerbosity(VerbosityLevel)
|
||||
self.verbose=VerbosityLevel or 0
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set track position time step.
|
||||
-- @param #WEAPON self
|
||||
-- @param #number TimeStep Time step in seconds when the position is updated. Default 0.01 sec ==> 100 evaluations per second.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:SetTimeStepTrack(TimeStep)
|
||||
self.dtTrack=TimeStep or 0.01
|
||||
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.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:SetMarkImpact(Switch)
|
||||
|
||||
if Switch==false then
|
||||
self.impactMark=false
|
||||
else
|
||||
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
|
||||
-- @param #function FuncTrack Function called during tracking.
|
||||
-- @param ... Optional function arguments.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:SetFuncTrack(FuncTrack, ...)
|
||||
self.trackFunc=FuncTrack
|
||||
self.trackArg=arg or {}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set callback function when weapon impacted or was destroyed otherwise, *i.e.* cannot be tracked any more.
|
||||
-- @param #WEAPON self
|
||||
-- @param #function FuncImpact Function called once the weapon impacted.
|
||||
-- @param ... Optional function arguments.
|
||||
-- @return #WEAPON self
|
||||
--
|
||||
-- @usage
|
||||
-- -- Function called on impact.
|
||||
-- local function OnImpact(Weapon)
|
||||
-- Weapon:GetImpactCoordinate():MarkToAll("Impact Coordinate of weapon")
|
||||
-- end
|
||||
--
|
||||
-- -- Set which function to call.
|
||||
-- myweapon:SetFuncImpact(OnImpact)
|
||||
--
|
||||
-- -- Start tracking.
|
||||
-- myweapon:Track()
|
||||
--
|
||||
function WEAPON:SetFuncImpact(FuncImpact, ...)
|
||||
self.impactFunc=FuncImpact
|
||||
self.impactArg=arg or {}
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the unit that launched the weapon.
|
||||
-- @param #WEAPON self
|
||||
-- @return Wrapper.Unit#UNIT Laucher unit.
|
||||
function WEAPON:GetLauncher()
|
||||
return self.launcherUnit
|
||||
end
|
||||
|
||||
--- Get the target, which the weapon is guiding to.
|
||||
-- @param #WEAPON self
|
||||
-- @return Wrapper.Object#OBJECT The target object, which can be a UNIT or STATIC object.
|
||||
function WEAPON:GetTarget()
|
||||
|
||||
local target=nil --Wrapper.Object#OBJECT
|
||||
|
||||
if self.weapon then
|
||||
|
||||
-- Get the DCS target object, which can be a Unit, Weapon, Static, Scenery, Airbase.
|
||||
local object=self.weapon:getTarget()
|
||||
|
||||
if object then
|
||||
|
||||
-- Get object category.
|
||||
local category=object:getCategory()
|
||||
|
||||
--Target name
|
||||
local name=object:getName()
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Got Target Object %s, category=%d", object:getName(), category))
|
||||
|
||||
if category==Object.Category.UNIT then
|
||||
|
||||
target=UNIT:FindByName(name)
|
||||
|
||||
elseif category==Object.Category.STATIC then
|
||||
|
||||
target=STATIC:FindByName(name, false)
|
||||
|
||||
elseif category==Object.Category.SCENERY then
|
||||
self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: Object category=%d is not implemented yet!", category))
|
||||
end
|
||||
|
||||
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
|
||||
-- @return DCS#Vec3 Velocity vector with x, y and z components in meters/second.
|
||||
function WEAPON:GetVelocityVec3()
|
||||
local Vvec3=nil
|
||||
if self.weapon then
|
||||
Vvec3=self.weapon:getVelocity()
|
||||
end
|
||||
return Vvec3
|
||||
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(ConversionFunction)
|
||||
|
||||
local speed=nil
|
||||
|
||||
if self.weapon then
|
||||
|
||||
local v=self:GetVelocityVec3()
|
||||
|
||||
speed=UTILS.VecNorm(v)
|
||||
|
||||
if ConversionFunction then
|
||||
speed=ConversionFunction(speed)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return speed
|
||||
end
|
||||
|
||||
--- Get the current 3D position vector.
|
||||
-- @param #WEAPON self
|
||||
-- @return DCS#Vec3 Current position vector in 3D.
|
||||
function WEAPON:GetVec3()
|
||||
|
||||
local vec3=nil
|
||||
if self.weapon then
|
||||
vec3=self.weapon:getPoint()
|
||||
end
|
||||
|
||||
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.
|
||||
function WEAPON:GetTypeName()
|
||||
return self.typeName
|
||||
end
|
||||
|
||||
--- Get coalition.
|
||||
-- @param #WEAPON self
|
||||
-- @return #number Coalition ID.
|
||||
function WEAPON:GetCoalition()
|
||||
return self.coalition
|
||||
end
|
||||
|
||||
--- Get country.
|
||||
-- @param #WEAPON self
|
||||
-- @return #number Country ID.
|
||||
function WEAPON:GetCountry()
|
||||
return self.country
|
||||
end
|
||||
|
||||
--- Get DCS object.
|
||||
-- @param #WEAPON self
|
||||
-- @return DCS#Weapon The weapon object.
|
||||
function WEAPON:GetDCSObject()
|
||||
-- This polymorphic function is used in Wrapper.Identifiable#IDENTIFIABLE
|
||||
return self.weapon
|
||||
end
|
||||
|
||||
--- Get the impact position vector. Note that this might not exist if the weapon has not impacted yet!
|
||||
-- @param #WEAPON self
|
||||
-- @return DCS#Vec3 Impact position vector (if any).
|
||||
function WEAPON:GetImpactVec3()
|
||||
return self.impactVec3
|
||||
end
|
||||
|
||||
--- Get the impact coordinate. Note that this might not exist if the weapon has not impacted yet!
|
||||
-- @param #WEAPON self
|
||||
-- @return Core.Point#COORDINATE Impact coordinate (if any).
|
||||
function WEAPON:GetImpactCoordinate()
|
||||
return self.impactCoord
|
||||
end
|
||||
|
||||
--- Check if weapon is in the air. Obviously not really useful for torpedos. Well, then again, this is DCS...
|
||||
-- @param #WEAPON self
|
||||
-- @return #boolean If `true`, weapon is in the air and `false` if not. Returns `nil` if weapon object itself is `nil`.
|
||||
function WEAPON:InAir()
|
||||
local inAir=nil
|
||||
if self.weapon then
|
||||
inAir=self.weapon:inAir()
|
||||
end
|
||||
return inAir
|
||||
end
|
||||
|
||||
|
||||
--- Check if weapon object (still) exists.
|
||||
-- @param #WEAPON self
|
||||
-- @return #boolean If `true`, the weapon object still exists and `false` otherwise. Returns `nil` if weapon object itself is `nil`.
|
||||
function WEAPON:IsExist()
|
||||
local isExist=nil
|
||||
if self.weapon then
|
||||
isExist=self.weapon:isExist()
|
||||
end
|
||||
return isExist
|
||||
end
|
||||
|
||||
|
||||
--- Check if weapon is a bomb.
|
||||
-- @param #WEAPON self
|
||||
-- @return #boolean If `true`, is a bomb.
|
||||
function WEAPON:IsBomb()
|
||||
return self.category==Weapon.Category.BOMB
|
||||
end
|
||||
|
||||
--- Check if weapon is a missile.
|
||||
-- @param #WEAPON self
|
||||
-- @return #boolean If `true`, is a missile.
|
||||
function WEAPON:IsMissile()
|
||||
return self.category==Weapon.Category.MISSILE
|
||||
end
|
||||
|
||||
--- Check if weapon is a rocket.
|
||||
-- @param #WEAPON self
|
||||
-- @return #boolean If `true`, is a missile.
|
||||
function WEAPON:IsRocket()
|
||||
return self.category==Weapon.Category.ROCKET
|
||||
end
|
||||
|
||||
--- Check if weapon is a shell.
|
||||
-- @param #WEAPON self
|
||||
-- @return #boolean If `true`, is a shell.
|
||||
function WEAPON:IsShell()
|
||||
return self.category==Weapon.Category.SHELL
|
||||
end
|
||||
|
||||
--- Check if weapon is a torpedo.
|
||||
-- @param #WEAPON self
|
||||
-- @return #boolean If `true`, is a torpedo.
|
||||
function WEAPON:IsTorpedo()
|
||||
return self.category==Weapon.Category.TORPEDO
|
||||
end
|
||||
|
||||
|
||||
--- Destroy the weapon object.
|
||||
-- @param #WEAPON self
|
||||
-- @param #number Delay Delay before destroy in seconds.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:Destroy(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, WEAPON.Destroy, self, 0)
|
||||
else
|
||||
if self.weapon then
|
||||
self:T(self.lid.."Destroying Weapon NOW!")
|
||||
self:StopTrack()
|
||||
self.weapon:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Start tracking the weapon until it impacts or is destroyed otherwise.
|
||||
-- The position of the weapon is monitored in small time steps. Once the position cannot be determined anymore, the monitoring is stopped and the last known position is
|
||||
-- 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.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:StartTrack(Delay)
|
||||
|
||||
-- Set delay before start.
|
||||
Delay=math.max(Delay or 0.001, 0.001)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Start tracking weapon in %.4f sec", Delay))
|
||||
|
||||
-- Weapon is not yet "alife" just yet. Start timer in 0.001 seconds.
|
||||
self.trackScheduleID=timer.scheduleFunction(WEAPON._TrackWeapon, self, timer.getTime() + Delay)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Stop tracking the weapon by removing the scheduler function.
|
||||
-- @param #WEAPON self
|
||||
-- @param #number Delay (Optional) Delay in seconds before the tracking is stopped.
|
||||
-- @return #WEAPON self
|
||||
function WEAPON:StopTrack(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
-- Delayed call.
|
||||
self:ScheduleOnce(Delay, WEAPON.StopTrack, self, 0)
|
||||
else
|
||||
|
||||
if self.trackScheduleID then
|
||||
|
||||
timer.removeFunction(self.trackScheduleID)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Track weapon until impact.
|
||||
-- @param #WEAPON self
|
||||
-- @param DCS#Time time Time in seconds.
|
||||
-- @return #number Time when called next or nil if not called again.
|
||||
function WEAPON:_TrackWeapon(time)
|
||||
|
||||
-- Debug info.
|
||||
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(
|
||||
function()
|
||||
local point=self.weapon:getPosition()
|
||||
return point
|
||||
end
|
||||
)
|
||||
|
||||
if status then
|
||||
|
||||
-------------------------------
|
||||
-- Weapon is still in exists --
|
||||
-------------------------------
|
||||
|
||||
-- Update last known position.
|
||||
self.pos3 = pos3
|
||||
|
||||
-- Update last known vec3.
|
||||
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))
|
||||
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(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
|
||||
|
||||
else
|
||||
|
||||
---------------------------
|
||||
-- Weapon does NOT exist --
|
||||
---------------------------
|
||||
|
||||
-- Get intercept point from position (p) and direction (x) in 50 meters.
|
||||
local ip = self:_GetIP(self.distIP)
|
||||
|
||||
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)
|
||||
|
||||
-- Mark coordinate.
|
||||
coord:MarkToAll("Intercept point")
|
||||
coord:SmokeBlue()
|
||||
|
||||
-- Distance to last known pos.
|
||||
local d=UTILS.VecDist3D(ip, self.vec3)
|
||||
|
||||
-- Output.
|
||||
self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters", d))
|
||||
|
||||
end
|
||||
|
||||
-- Safe impact vec3.
|
||||
self.impactVec3=ip or self.vec3
|
||||
|
||||
-- Safe impact coordinate.
|
||||
self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
|
||||
|
||||
-- Mark impact point on F10 map.
|
||||
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 {}))
|
||||
end
|
||||
|
||||
-- Stop tracking by returning nil below.
|
||||
self.tracking=false
|
||||
|
||||
end
|
||||
|
||||
-- Return next time the function is called or nil to stop the scheduler.
|
||||
if self.tracking then
|
||||
if self.dtTrack and self.dtTrack>0.001 then
|
||||
return time+self.dtTrack
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Compute estimated intercept/impact point (IP) based on last known position and direction.
|
||||
-- @param #WEAPON self
|
||||
-- @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)
|
||||
|
||||
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
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -43,6 +43,7 @@ Wrapper/Static.lua
|
||||
Wrapper/Airbase.lua
|
||||
Wrapper/Scenery.lua
|
||||
Wrapper/Marker.lua
|
||||
Wrapper/Weapon.lua
|
||||
|
||||
Cargo/Cargo.lua
|
||||
Cargo/CargoUnit.lua
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user