Merge branch 'develop' into FF/Ops

This commit is contained in:
Frank 2025-10-25 21:17:01 +02:00
commit 654f752ca0
20 changed files with 1120 additions and 798 deletions

View File

@ -577,13 +577,19 @@ do -- Zones and Pathlines
-- For a rectangular polygon drawing, we have the width (y) and height (x). -- For a rectangular polygon drawing, we have the width (y) and height (x).
local w=objectData.width local w=objectData.width
local h=objectData.height local h=objectData.height
local rotation = UTILS.ToRadian(objectData.angle or 0)
-- Create points from center using with and height (width for y and height for x is a bit confusing, but this is how ED implemented it). local sinRot = math.sin(rotation)
local points={} local cosRot = math.cos(rotation)
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left local dx = h / 2
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right local dy = w / 2
points[3]={x=vec2.x+h/2, y=vec2.y-w/2} --Lower right
points[4]={x=vec2.x-h/2, y=vec2.y-w/2} --Lower left local points = {
{ x = -dx * cosRot - (-dy * sinRot) + vec2.x, y = -dx * sinRot + (-dy * cosRot) + vec2.y },
{ x = dx * cosRot - (-dy * sinRot) + vec2.x, y = dx * sinRot + (-dy * cosRot) + vec2.y },
{ x = dx * cosRot - (dy * sinRot) + vec2.x, y = dx * sinRot + (dy * cosRot) + vec2.y },
{ x = -dx * cosRot - (dy * sinRot) + vec2.x, y = -dx * sinRot + (dy * cosRot) + vec2.y },
}
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY") --local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")

View File

@ -7865,6 +7865,28 @@ do -- SET_OPSGROUP
return self return self
end end
--- Iterate the SET_OPSGROUP and count how many GROUPs and UNITs are alive.
-- @param #SET_GROUP self
-- @return #number The number of GROUPs alive.
-- @return #number The number of UNITs alive.
function SET_OPSGROUP:CountAlive()
local CountG = 0
local CountU = 0
local Set = self:GetSet()
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
if GroupData and GroupData:IsAlive() then
CountG = CountG + 1
-- Count Units.
CountU = CountU + GroupData:GetGroup():CountAliveUnits()
end
end
return CountG, CountU
end
--- Finds an OPSGROUP based on the group name. --- Finds an OPSGROUP based on the group name.
-- @param #SET_OPSGROUP self -- @param #SET_OPSGROUP self
-- @param #string GroupName Name of the group. -- @param #string GroupName Name of the group.

View File

@ -633,7 +633,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
if self.StaticCopyFrom ~= nil then if self.StaticCopyFrom ~= nil then
mystatic.StaticCopyFrom = self.StaticCopyFrom mystatic.StaticCopyFrom = self.StaticCopyFrom
if not _DATABASE.Templates.Statics[Template.name] then end
local TemplateGroup={} local TemplateGroup={}
TemplateGroup.units={} TemplateGroup.units={}
TemplateGroup.units[1]=Template TemplateGroup.units[1]=Template
@ -641,8 +642,6 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
TemplateGroup.y=Template.y TemplateGroup.y=Template.y
TemplateGroup.name=Template.name TemplateGroup.name=Template.name
_DATABASE:_RegisterStaticTemplate( TemplateGroup, self.CoalitionID, self.CategoryID, CountryID ) _DATABASE:_RegisterStaticTemplate( TemplateGroup, self.CoalitionID, self.CategoryID, CountryID )
end
end
return mystatic return mystatic
end end

View File

@ -1934,6 +1934,21 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset)
return self return self
end end
--- Updates the current location from a @{Wrapper.Group}.
-- @param #ZONE_UNIT self
-- @param Wrapper.Group#GROUP Group (optional) Update from this Unit, if nil, update from the UNIT this zone is based on.
-- @return self
function ZONE_UNIT:UpdateFromUnit(Unit)
if Unit and Unit:IsAlive() then
local vec2 = Unit:GetVec2()
self.LastVec2 = vec2
elseif self.ZoneUNIT and self.ZoneUNIT:IsAlive() then
local ZoneVec2 = self.ZoneUNIT:GetVec2()
self.LastVec2 = ZoneVec2
end
return self
end
--- Returns the current location of the @{Wrapper.Unit#UNIT}. --- Returns the current location of the @{Wrapper.Unit#UNIT}.
-- @param #ZONE_UNIT self -- @param #ZONE_UNIT self
@ -2071,6 +2086,22 @@ function ZONE_GROUP:GetVec2()
return ZoneVec2 return ZoneVec2
end end
--- Updates the current location from a @{Wrapper.Group}.
-- @param #ZONE_GROUP self
-- @param Wrapper.Group#GROUP Group (optional) Update from this Group, if nil, update from the GROUP this zone is based on.
-- @return self
function ZONE_GROUP:UpdateFromGroup(Group)
if Group and Group:IsAlive() then
local vec2 = Group:GetVec2()
self.Vec2 = vec2
elseif self._.ZoneGROUP and self._.ZoneGROUP:IsAlive() then
local ZoneVec2 = self._.ZoneGROUP:GetVec2()
self.Vec2 = ZoneVec2
self._.ZoneVec2Cache = ZoneVec2
end
return self
end
--- Returns a random location within the zone of the @{Wrapper.Group}. --- Returns a random location within the zone of the @{Wrapper.Group}.
-- @param #ZONE_GROUP self -- @param #ZONE_GROUP self
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location. -- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.

View File

@ -321,7 +321,9 @@ function SCORING:New( GameName, SavePath, AutoSave )
-- Create the CSV file. -- Create the CSV file.
self.AutoSavePath = SavePath self.AutoSavePath = SavePath
self.AutoSave = AutoSave or true self.AutoSave = AutoSave or true
if self.AutoSave == true then
self:OpenCSV( GameName ) self:OpenCSV( GameName )
end
return self return self
@ -1935,7 +1937,7 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
TargetUnitType = TargetUnitType or "" TargetUnitType = TargetUnitType or ""
TargetUnitName = TargetUnitName or "" TargetUnitName = TargetUnitName or ""
if lfs and io and os and self.AutoSave then if lfs and io and os and self.AutoSave == true and self.CSVFile ~= nil then
self.CSVFile:write( self.CSVFile:write(
'"' .. self.GameName .. '"' .. ',' .. '"' .. self.GameName .. '"' .. ',' ..
'"' .. self.RunTime .. '"' .. ',' .. '"' .. self.RunTime .. '"' .. ',' ..

View File

@ -33,7 +33,7 @@
-- @module Functional.Tiresias -- @module Functional.Tiresias
-- @image Functional.Tiresias.jpg -- @image Functional.Tiresias.jpg
--- Last Update: July 2025 --- Last Update: Oct 2025
--- **TIRESIAS** class, extends Core.Base#BASE --- **TIRESIAS** class, extends Core.Base#BASE
-- @type TIRESIAS -- @type TIRESIAS
@ -104,8 +104,8 @@
-- @field #TIRESIAS -- @field #TIRESIAS
TIRESIAS = { TIRESIAS = {
ClassName = "TIRESIAS", ClassName = "TIRESIAS",
debug = true, debug = false,
version = " 0.0.7-OPT" , version = " 0.0.8" ,
Interval = 20, Interval = 20,
GroundSet = nil, GroundSet = nil,
VehicleSet = nil, VehicleSet = nil,
@ -140,7 +140,7 @@ function TIRESIAS:New()
self:AddTransition("*", "Status", "*") -- TIRESIAS status update. self:AddTransition("*", "Status", "*") -- TIRESIAS status update.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self.ExceptionSet = nil --SET_GROUP:New():Clear(false) self.ExceptionSet = SET_GROUP:New() --:Clear(false)
self._cached_zones = {} -- Initialize zone cache self._cached_zones = {} -- Initialize zone cache
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
@ -224,10 +224,10 @@ function TIRESIAS:AddExceptionSet(Set)
Set:ForEachGroupAlive( Set:ForEachGroupAlive(
function(grp) function(grp)
local inAAASet = self.AAASet:IsIncludeObject(grp) --local inAAASet = self.AAASet:IsIncludeObject(grp)
local inVehSet = self.VehicleSet:IsIncludeObject(grp) --local inVehSet = self.VehicleSet:IsIncludeObject(grp)
local inSAMSet = self.SAMSet:IsIncludeObject(grp) --local inSAMSet = self.SAMSet:IsIncludeObject(grp)
if grp:IsGround() and (not grp.Tiresias) and (not inAAASet) and (not inVehSet) and (not inSAMSet) then if grp:IsGround() and (not grp.Tiresias) then --and (not inAAASet) and (not inVehSet) and (not inSAMSet) then
grp.Tiresias = exception_data grp.Tiresias = exception_data
exceptions:AddGroup(grp, true) exceptions:AddGroup(grp, true)
BASE:T(" TIRESIAS: Added exception group: " .. grp:GetName()) BASE:T(" TIRESIAS: Added exception group: " .. grp:GetName())
@ -419,8 +419,8 @@ function TIRESIAS:_SwitchOnGroups(group, radius)
-- Use cached zones to reduce object creation -- Use cached zones to reduce object creation
local group_name = group:GetName() local group_name = group:GetName()
local cache_key = group_name .. " _" .. radius local cache_key = group_name .. " _" .. radius
local zone = self._cached_zones[cache_key] local zone = self._cached_zones[cache_key] -- Core.Zone#ZONE_RADIUS
local ground = self._cached_groupsets[cache_key] --local ground = self._cached_groupsets[cache_key] -- Core.Set#SET_GROUP
if not zone then if not zone then
zone = ZONE_GROUP:New(" Zone-" .. group_name, group, UTILS.NMToMeters(radius)) zone = ZONE_GROUP:New(" Zone-" .. group_name, group, UTILS.NMToMeters(radius))
@ -430,12 +430,14 @@ function TIRESIAS:_SwitchOnGroups(group, radius)
zone:UpdateFromGroup(group) zone:UpdateFromGroup(group)
end end
if not ground then --if not ground then
ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce() --ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce()
self._cached_groupsets[cache_key] = ground --self._cached_groupsets[cache_key] = ground
else --else
ground:FilterZones({zone},true):FilterOnce() --ground:FilterZones({zone},true):FilterOnce()
end zone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
local ground = zone:GetScannedSetGroup()
--end
local count = ground:CountAlive() local count = ground:CountAlive()

View File

@ -6950,6 +6950,9 @@ function AIRBOSS:_AddMarshalGroup( flight, stack )
-- Convert to clock string. -- Convert to clock string.
local Ccharlie = UTILS.SecondsToClock( flight.Tcharlie ) local Ccharlie = UTILS.SecondsToClock( flight.Tcharlie )
-- Make sure brc is never above 360
brc = brc % 360
-- Combined marshal call. -- Combined marshal call.
self:_MarshalCallArrived( flight.onboard, flight.case, brc, alt, Ccharlie, P ) self:_MarshalCallArrived( flight.onboard, flight.case, brc, alt, Ccharlie, P )
@ -7603,7 +7606,7 @@ function AIRBOSS:_InitPlayer( playerData, step )
playerData.landed = false playerData.landed = false
playerData.Tlso = timer.getTime() playerData.Tlso = timer.getTime()
playerData.Tgroove = nil playerData.Tgroove = nil
playerData.TIG0 = nil playerData.TIG0 = 0 --changed to prevent errors in script when player is not in correct spot
playerData.wire = nil playerData.wire = nil
playerData.flag = -100 playerData.flag = -100
playerData.debriefschedulerID = nil playerData.debriefschedulerID = nil
@ -8142,7 +8145,6 @@ end
--- Check current player status. --- Check current player status.
-- @param #AIRBOSS self -- @param #AIRBOSS self
function AIRBOSS:_CheckPlayerStatus() function AIRBOSS:_CheckPlayerStatus()
-- Loop over all players. -- Loop over all players.
for _playerName, _playerData in pairs( self.players ) do for _playerName, _playerData in pairs( self.players ) do
local playerData = _playerData -- #AIRBOSS.PlayerData local playerData = _playerData -- #AIRBOSS.PlayerData
@ -9644,7 +9646,7 @@ end
--- Break entry for case I/II recoveries. --- Break entry for case I/II recoveries.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #AIRBOSS.PlayerData playerData Player data table. -- @param #AIRBOSS.PlayerData playerData Player data table.
function AIRBOSS:_BreakEntry( playerData ) --Adam Edits begin 7/24/23 function AIRBOSS:_BreakEntry( playerData )
-- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier) -- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier)
local X, Z = self:_GetDistances( playerData.unit ) local X, Z = self:_GetDistances( playerData.unit )
@ -9655,111 +9657,16 @@ function AIRBOSS:_BreakEntry( playerData ) --Adam Edits begin 7/24/23
return return
end end
local stern = self:_GetSternCoord()
local coord = playerData.unit:GetCoordinate()
local dist = coord:Get2DDistance( stern )
--adam edits
local playerCallsign = playerData.unit:GetCallsign()
--trigger.action.outText(' Hornet is hook down on pre-break entry for testing hook argument ', 5)
--trigger.action.outText(' Hornet callsign is '..playerCallsign, 5)
local playerName = playerData.name
local unit = playerData.unit
--local playerName = unit:GetName()
--trigger.action.outText(' Hornet name is '..playerName, 5)
local unitClient = Unit.getByName(unit:GetName())
local hookArgument = unitClient:getDrawArgumentValue(25)
local hookArgument_Tomcat = unitClient:getDrawArgumentValue(1305)
local speedMPS = playerData.unit:GetVelocityMPS()
local speedKTS = UTILS.MpsToKnots( speedMPS )
local player_alt = playerData.unit:GetAltitude()
player_alt_feet = player_alt * 3.28
player_alt_feet = player_alt_feet/10
player_alt_feet = math.floor(player_alt_feet)*10
local player_velocity_round = speedKTS * 1.00
player_velocity_round = player_velocity_round/10
player_velocity_round = math.floor(player_velocity_round)*10
local player_alt_feet = player_alt * 3.28
player_alt_feet = player_alt_feet/10
player_alt_feet = math.floor(player_alt_feet)*10
local Play_SH_Sound = USERSOUND:New( "Airboss Soundfiles/GreatBallsOfFire.ogg" )
local Play_666SH_Sound = USERSOUND:New( "Airboss Soundfiles/Runninwiththedevil.ogg" )
local playerType = playerData.actype
if dist <1000 and clientSHBFlag == false then
if speedKTS > 450 and speedKTS < 590 then
if player_alt_feet < 1500 then
if hookArgument > 0 or hookArgument_Tomcat > 0 then
--trigger.action.outText(' 1 - Hornet is hook down so SHB!!!! Hook argument is: '..hookArgument, 5)
playerData.shb = true
trigger.action.outText(playerName..' performing a Sierra Hotel Break in a '..playerType, 10)
local sh_message_to_discord = ('**'..playerName..' is performing a Sierra Hotel Break in a '..playerType..' at '..player_velocity_round..' knots and '..player_alt_feet..' feet!**')
HypeMan.sendBotMessage(sh_message_to_discord)
Play_SH_Sound:ToAll()
clientSHBFlag = true
else
--trigger.action.outText(' Hornet is hook up on initial and just fast so no SHB. Hook argument is: '..hookArgument, 5)
playerData.shb = false
end
-- Next step: Early Break.
else
end
elseif speedKTS > 589 then
if player_alt_feet < 625 and player_alt_feet >575 then --SHB 666
if hookArgument > 0 or hookArgument_Tomcat > 0 then
--trigger.action.outText(' 1 - Hornet is hook down so SHB!!!! Hook argument is: '..hookArgument, 5)
playerData.shb = true
trigger.action.outText(playerName..' performing a 666 Sierra Hotel Break in a '..playerType, 10)
local sh_message_to_discord = ('**'..playerName..' is performing a 666 Sierra Hotel Break in a '..playerType..' at '..player_velocity_round..' knots and '..player_alt_feet..' feet!**')
HypeMan.sendBotMessage(sh_message_to_discord)
Play_666SH_Sound:ToAll()
clientSHBFlag = true
else
--trigger.action.outText(' Hornet is hook up on initial and just fast so no SHB. Hook argument is: '..hookArgument, 5)
playerData.shb = false
end
else
if hookArgument > 0 or hookArgument_Tomcat > 0 then
--trigger.action.outText(' 1 - Hornet is hook down so SHB!!!! Hook argument is: '..hookArgument, 5)
playerData.shb = true
trigger.action.outText(playerName..' performing a Sierra Hotel Break in a '..playerType, 10)
local sh_message_to_discord = ('**'..playerName..' is performing a Sierra Hotel Break in a '..playerType..' at '..player_velocity_round..' knots and '..player_alt_feet..' feet!**')
HypeMan.sendBotMessage(sh_message_to_discord)
Play_SH_Sound:ToAll()
clientSHBFlag = true
else
--trigger.action.outText(' Hornet is hook up on initial and just fast so no SHB. Hook argument is: '..hookArgument, 5)
playerData.shb = false
end
end
else
--trigger.action.outText(' Hornet is less than 400 kts so not SHB.... ', 5)
end
else
--trigger.action.outText(' ******TEST OF of Break Entry and distance to CVN is: '..dist, 5)
end
-- Check if we are in front of the boat (diffX > 0). -- Check if we are in front of the boat (diffX > 0).
if self:_CheckLimits( X, Z, self.BreakEntry ) then if self:_CheckLimits( X, Z, self.BreakEntry ) then
--trigger.action.outText(' 2 - Hornet is hook down on break entry for testing hook argument ', 5)
-- Hint for player about altitude, AoA etc. -- Hint for player about altitude, AoA etc.
self:_PlayerHint( playerData ) self:_PlayerHint( playerData )
-- Next step: Early Break.
self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.EARLYBREAK ) self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.EARLYBREAK )
clientSHBFlag = false
end end
end--Adam Edits end 7/24/23 end
--- Break. --- Break.
-- @param #AIRBOSS self -- @param #AIRBOSS self
@ -12051,10 +11958,12 @@ function AIRBOSS:GetHeading( magnetic )
hdg = hdg - self.magvar hdg = hdg - self.magvar
end end
-- Adjust negative values. -- -- Adjust negative values.
if hdg < 0 then -- if hdg < 0 then
hdg = hdg + 360 -- hdg = hdg + 360
end -- end
hdg = hdg % 360 -- using this to replace the above function to prevent negative values and BRC higher than 360
return hdg return hdg
end end
@ -12321,7 +12230,7 @@ function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord )
-- Ship heading so cross wind is min for the given wind. -- Ship heading so cross wind is min for the given wind.
-- local intowind = (540 + (windto - magvar + math.deg(theta) )) % 360 -- VNAO Edit: Using old heading into wind algorithm -- local intowind = (540 + (windto - magvar + math.deg(theta) )) % 360 -- VNAO Edit: Using old heading into wind algorithm
local intowind = self:GetHeadingIntoWind_old(vdeck) -- VNAO Edit: Using old heading into wind algorithm local intowind = self:GetHeadingIntoWind_old(vdeck,magnetic) -- VNAO Edit: Using old heading into wind algorithm
return intowind, v return intowind, v
end end
@ -12751,7 +12660,7 @@ function AIRBOSS:_LSOgrade( playerData )
local TIG = "" local TIG = ""
-- Analyse flight data and convert to LSO text. -- Analyse flight data and convert to LSO text.
if playerData.Tgroove and playerData.Tgroove <= 360 and playerData.case < 3 then --Circuit Added if playerData.Tgroove and playerData.Tgroove <= 360 and playerData.case < 3 then --Circuit Added
TIG = self:_EvalGrooveTime( playerData ) --Circuit Added TIG = self:_EvalGrooveTime( playerData ) or "N/A" --Circuit Added
end --Circuit Added end --Circuit Added
local GXX, nXX = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.XX ) local GXX, nXX = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.XX )
local GIM, nIM = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.IM ) local GIM, nIM = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.IM )
@ -12945,16 +12854,21 @@ function AIRBOSS:_LSOgrade( playerData )
end end
-- VNAO EDIT: Subtract 1pt from overall grade if it is a 1 wire. If it's already a 1pt pass, ignore. -- -- VNAO EDIT: Subtract 1pt from overall grade if it is a 1 wire. If it's already a 1pt pass, ignore.
if playerData.wire == 1 and points > 1 then -- VNAO EDIT: added -- if playerData.wire == 1 and points > 1 then -- VNAO EDIT: added
if points == 4 then -- VNAO EDIT: added -- if points == 4 then -- VNAO EDIT: added
points = 3 -- VNAO EDIT: added -- points = 3 -- VNAO EDIT: added
grade = "(OK)" -- VNAO EDIT: added -- grade = "(OK)" -- VNAO EDIT: added
elseif points == 3 then -- VNAO EDIT: added -- elseif points == 3 then -- VNAO EDIT: added
points = 2 -- VNAO EDIT: added -- points = 2 -- VNAO EDIT: added
grade = "--" -- VNAO EDIT: added -- grade = "--" -- VNAO EDIT: added
end -- VNAO EDIT: added -- end -- VNAO EDIT: added
end -- VNAO EDIT: added -- end -- VNAO EDIT: added
-- Circuit edit only take points awary from a 1 wire if there are more than 4 other deviations
if playerData.wire == 1 and points >= 3 and N > 4 then
points = points -1
end
env.info("Returning: " .. grade .. " " .. points .. " " .. G) env.info("Returning: " .. grade .. " " .. points .. " " .. G)
@ -13030,6 +12944,7 @@ function AIRBOSS:_Flightdata2Text( playerData, groovestep )
-- Speed via AoA. Depends on aircraft type. -- Speed via AoA. Depends on aircraft type.
local S = nil local S = nil
local A = nil --circuit moved this line to be seen outside of this scope
if step~=AIRBOSS.PatternStep.GROOVE_IW then -- VNAO Edit - Added To avoid getting an AOA or GS grade in the wires... let's just check left or right in the wires if step~=AIRBOSS.PatternStep.GROOVE_IW then -- VNAO Edit - Added To avoid getting an AOA or GS grade in the wires... let's just check left or right in the wires
if AIRBOSS.PatternStep.GROOVE_AR and playerData.waveoff == true and playerData.owo == true then -- VNAO Edit - Added if AIRBOSS.PatternStep.GROOVE_AR and playerData.waveoff == true and playerData.owo == true then -- VNAO Edit - Added
-- env.info('Adam MOOSE Edit -AR and waved off so do not add AOA or GS errors to comments ') -- VNAO Edit - Added -- env.info('Adam MOOSE Edit -AR and waved off so do not add AOA or GS errors to comments ') -- VNAO Edit - Added
@ -13050,7 +12965,7 @@ function AIRBOSS:_Flightdata2Text( playerData, groovestep )
end end
-- Glideslope/altitude. Good [-0.3, 0.4] asymmetric! -- Glideslope/altitude. Good [-0.3, 0.4] asymmetric!
local A = nil
if GSE > self.gle.HIGH then if GSE > self.gle.HIGH then
A = underline( "H" ) A = underline( "H" )
elseif GSE > self.gle.High then elseif GSE > self.gle.High then

View File

@ -2324,8 +2324,9 @@ end
-- @param #number Speed Speed in knots. -- @param #number Speed Speed in knots.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL. -- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #string Formation Formation used by ground units during patrol. Default "Off Road". -- @param #string Formation Formation used by ground units during patrol. Default "Off Road".
-- @param #number StayInZoneTime Stay this many seconds in the zone when done, only then drive back.
-- @return #AUFTRAG self -- @return #AUFTRAG self
function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation) function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation, StayInZoneTime)
local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE) local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
@ -2339,6 +2340,7 @@ function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROE=ENUMS.ROE.ReturnFire
mission.optionROT=ENUMS.ROT.PassiveDefense mission.optionROT=ENUMS.ROT.PassiveDefense
mission.optionAlarm=ENUMS.AlarmState.Auto mission.optionAlarm=ENUMS.AlarmState.Auto
mission.StayInZoneTime = StayInZoneTime
mission.missionFraction=0.1 mission.missionFraction=0.1
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
@ -4016,6 +4018,23 @@ function AUFTRAG:IsOver()
return over return over
end end
--- Check if mission is repeatable.
-- @param #AUFTRAG self
-- @return #boolean If true, mission is repeatable.
function AUFTRAG:IsRepeatable()
local repeatmeS=self.repeatedSuccess<self.NrepeatSuccess or self.repeated<self.Nrepeat
local repeatmeF=self.repeatedFailure<self.NrepeatFailure or self.repeated<self.Nrepeat
if repeatmeS==true or repeatmeF==true then return true else return false end
return false
end
--- Check if mission is NOT repeatable.
-- @param #AUFTRAG self
-- @return #boolean If true, mission is NOT repeatable.
function AUFTRAG:IsNotRepeatable()
return not self:IsRepeatable()
end
--- Check if mission is NOT over. --- Check if mission is NOT over.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @return #boolean If true, mission is NOT over yet. -- @return #boolean If true, mission is NOT over yet.

View File

@ -509,7 +509,7 @@ do
-- @field #AWACS -- @field #AWACS
AWACS = { AWACS = {
ClassName = "AWACS", -- #string ClassName = "AWACS", -- #string
version = "0.2.72", -- #string version = "0.2.73", -- #string
lid = "", -- #string lid = "", -- #string
coalition = coalition.side.BLUE, -- #number coalition = coalition.side.BLUE, -- #number
coalitiontxt = "blue", -- #string coalitiontxt = "blue", -- #string
@ -1596,6 +1596,16 @@ function AWACS:SetLocale(Locale)
return self return self
end end
--- [User] Set own coordinate for BullsEye.
-- @param #AWACS self
-- @param Core.Point#COORDINATE
-- @return #AWACS self
function AWACS:SetBullsCoordinate(Coordinate)
self:T(self.lid.."SetBullsCoordinate")
self.AOCoordinate = Coordinate
return self
end
--- [User] Set the max mission range flights can be away from their home base. --- [User] Set the max mission range flights can be away from their home base.
-- @param #AWACS self -- @param #AWACS self
-- @param #number NM Distance in nautical miles -- @param #number NM Distance in nautical miles

View File

@ -31,7 +31,7 @@
-- @image OPS_CSAR.jpg -- @image OPS_CSAR.jpg
--- ---
-- Last Update July 2025 -- Last Update Oct 2025
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@ -315,7 +315,7 @@ CSAR.AircraftType["CH-47Fbl1"] = 31
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version="1.0.33" CSAR.version="1.0.34"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@ -1150,11 +1150,11 @@ function CSAR:_EventHandler(EventData)
local initdcscoord = nil local initdcscoord = nil
local initcoord = nil local initcoord = nil
if _event.id == EVENTS.Ejection then if _event.id == EVENTS.Ejection and _event.TgtDCSUnit then
initdcscoord = _event.TgtDCSUnit:getPoint() initdcscoord = _event.TgtDCSUnit:getPoint()
initcoord = COORDINATE:NewFromVec3(initdcscoord) initcoord = COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord}) self:T({initdcscoord})
else elseif _event.IniDCSUnit then
initdcscoord = _event.IniDCSUnit:getPoint() initdcscoord = _event.IniDCSUnit:getPoint()
initcoord = COORDINATE:NewFromVec3(initdcscoord) initcoord = COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord}) self:T({initdcscoord})
@ -1244,7 +1244,10 @@ function CSAR:_EventHandler(EventData)
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
self:__Landed(2,_event.IniUnitName, _place) self:__Landed(2,_event.IniUnitName, _place)
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true,true) local IsHeloBase = false
local ABName = _place:GetName()
if ABName and string.find(ABName,"^H") then IsHeloBase = true end -- if name starts with an H it's an (possibly elevated) helo base on current maps
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true,true,IsHeloBase)
else else
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition())) self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
end end
@ -1731,8 +1734,9 @@ end
-- @param #string heliname Heli name -- @param #string heliname Heli name
-- @param #string groupname Group name -- @param #string groupname Group name
-- @param #boolean isairport If true, EVENT.Landing took place at an airport or FARP -- @param #boolean isairport If true, EVENT.Landing took place at an airport or FARP
-- @param #boolean noreschedule If true, do not try to reschedule this is distances are not ok (coming from landing event) -- @param #boolean noreschedule If true, do not try to reschedule this if distances are not ok (coming from landing event)
function CSAR:_ScheduledSARFlight(heliname,groupname, isairport, noreschedule) -- @param #boolean IsHeloBase If true, landing took place at a Helo Base (name "H ..." on current maps)
function CSAR:_ScheduledSARFlight(heliname,groupname, isairport, noreschedule, IsHeloBase)
self:T(self.lid .. " _ScheduledSARFlight") self:T(self.lid .. " _ScheduledSARFlight")
self:T({heliname,groupname}) self:T({heliname,groupname})
local _heliUnit = self:_GetSARHeli(heliname) local _heliUnit = self:_GetSARHeli(heliname)
@ -1758,7 +1762,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname, isairport, noreschedule)
self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Distance km: "..math.floor(_dist/1000)) self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Distance km: "..math.floor(_dist/1000))
if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then if ( _dist < self.FARPRescueDistance or isairport ) and ((_heliUnit:InAir() == false) or (IsHeloBase == true)) then
self:T(self.lid.."[Drop off debug] Distance ok, door check") self:T(self.lid.."[Drop off debug] Distance ok, door check")
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true, true)
@ -1773,7 +1777,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname, isairport, noreschedule)
--queue up --queue up
if not noreschedule then if not noreschedule then
self:__Returning(5,heliname,_woundedGroupName, isairport) self:__Returning(5,heliname,_woundedGroupName, isairport)
self:ScheduleOnce(5,self._ScheduledSARFlight,self,heliname,groupname, isairport, noreschedule) self:ScheduleOnce(5,self._ScheduledSARFlight,self,heliname,groupname, isairport, noreschedule, IsHeloBase)
end end
return self return self
end end

View File

@ -25,7 +25,7 @@
-- @module Ops.CTLD -- @module Ops.CTLD
-- @image OPS_CTLD.jpg -- @image OPS_CTLD.jpg
-- Last Update July 2025 -- Last Update Oct 2025
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -147,6 +147,7 @@ CTLD_CARGO = {
Location = ZONE:New(Location) Location = ZONE:New(Location)
end end
self.Location = Location self.Location = Location
self.NoMoveToZone = false
return self return self
end end
@ -783,6 +784,7 @@ do
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10) -- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10)
-- -- additionally, you can limit **where** the stock is available (one location only!) - this one is available in a zone called "Vehicle Store". -- -- additionally, you can limit **where** the stock is available (one location only!) - this one is available in a zone called "Vehicle Store".
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10,nil,nil,"Vehicle Store") -- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10,nil,nil,"Vehicle Store")
-- -- Tip: if you want the spawned/built group NOT to move to a MOVE zone, replace AddCratesCargo with AddCratesCargoNoMove (same parameters).
-- --
-- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build: -- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build:
-- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4) -- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4)
@ -1556,6 +1558,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
self.smokedistance = 2000 self.smokedistance = 2000
self.movetroopstowpzone = true self.movetroopstowpzone = true
self.movetroopsdistance = 5000 self.movetroopsdistance = 5000
self.returntroopstobase = true -- if set to false, troops would stay after deployment inside a load zone.
self.troopdropzoneradius = 100 self.troopdropzoneradius = 100
self.VehicleMoveFormation = AI.Task.VehicleFormation.VEE self.VehicleMoveFormation = AI.Task.VehicleFormation.VEE
@ -3676,7 +3679,7 @@ function CTLD:_UnloadTroops(Group, Unit)
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end end
if inzone then if inzone then
droppingatbase = true droppingatbase = self.returntroopstobase
end end
-- check for hover unload -- check for hover unload
local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters
@ -4513,7 +4516,8 @@ function CTLD:_RefreshF10Menus()
end end
for _,cargoObj in pairs(self.Cargo_Crates) do for _,cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -4524,7 +4528,8 @@ function CTLD:_RefreshF10Menus()
end end
for _,cargoObj in pairs(self.Cargo_Statics) do for _,cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -4536,7 +4541,8 @@ function CTLD:_RefreshF10Menus()
else else
for _,cargoObj in pairs(self.Cargo_Crates) do for _,cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -4547,7 +4553,8 @@ function CTLD:_RefreshF10Menus()
end end
for _,cargoObj in pairs(self.Cargo_Statics) do for _,cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -4559,14 +4566,15 @@ function CTLD:_RefreshF10Menus()
end end
end end
else else
if self.usesubcats then if self.usesubcats == true then
local subcatmenus = {} local subcatmenus = {}
for catName, _ in pairs(self.subcats) do for catName, _ in pairs(self.subcats) do
subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case
end end
for _, cargoObj in pairs(self.Cargo_Crates) do for _, cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -4575,7 +4583,8 @@ function CTLD:_RefreshF10Menus()
end end
for _, cargoObj in pairs(self.Cargo_Statics) do for _, cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -4585,7 +4594,8 @@ function CTLD:_RefreshF10Menus()
else else
for _, cargoObj in pairs(self.Cargo_Crates) do for _, cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -4594,7 +4604,8 @@ function CTLD:_RefreshF10Menus()
end end
for _, cargoObj in pairs(self.Cargo_Statics) do for _, cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock() local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@ -5134,7 +5145,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP) inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP)
end end
if inzone then if inzone then
droppingatbase = true droppingatbase = self.returntroopstobase
end end
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
@ -5426,6 +5437,52 @@ function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,Sub
return self return self
end end
--- Identical to AddCratesCargo, but registers the cargo so the spawned/built group does not move to MOVE zones.
--- User function - Add *generic* crate-type loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built.
-- @param #CTLD self
-- @param #string Name Unique name of this type of cargo. E.g. "Humvee".
-- @param #table Templates Table of #string names of late activated Wrapper.Group#GROUP building this cargo.
-- @param #CTLD_CARGO.Enum Type Type of cargo. I.e. VEHICLE or FOB. VEHICLE will move to destination zones when dropped/build, FOB stays put.
-- @param #number NoCrates Number of crates needed to build this cargo.
-- @param #number PerCrateMass Mass in kg of each crate
-- @param #number Stock Number of buildable groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of sub-category (optional).
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu.
-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string.
-- @param #string UnitTypes Unit type names (optional). If set, only these unit types can pick up the cargo, e.g. "UH-1H" or {"UH-1H","OH58D"}.
-- @param #string Category Static category name (optional). If set, spawn cargo crate with an alternate category type, e.g. "Cargos".
-- @param #string TypeName Static type name (optional). If set, spawn cargo crate with an alternate type shape, e.g. "iso_container".
-- @param #string ShapeName Static shape name (optional). If set, spawn cargo crate with an alternate type sub-shape, e.g. "iso_container_cargo".
-- @return #CTLD self
function CTLD:AddCratesCargoNoMove(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location,UnitTypes,Category,TypeName,ShapeName)
self:T(self.lid .. " AddCratesCargoNoMove")
if not self:_CheckTemplates(Templates) then
self:E(self.lid .. "Crates Cargo for " .. Name .. " has missing template(s)!" )
return self
end
self.CargoCounter = self.CargoCounter + 1
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
cargo.NoMoveToZone = true
if UnitTypes then
cargo:AddUnitTypeName(UnitTypes)
end
cargo:SetStaticTypeAndShape("Cargos",self.basetype)
if TypeName then
cargo:SetStaticTypeAndShape(Category,TypeName,ShapeName)
end
table.insert(self.Cargo_Crates,cargo)
self.templateToCargoName = self.templateToCargoName or {}
if type(Templates)=="table" then
for _,t in pairs(Templates) do self.templateToCargoName[t] = Name end
else
self.templateToCargoName[Templates] = Name
end
self.nomovetozone_names = self.nomovetozone_names or {}
self.nomovetozone_names[Name] = true
if SubCategory and self.usesubcats ~= true then self.usesubcats=true end
return self
end
--- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped. --- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped.
-- @param #CTLD self -- @param #CTLD self
-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1". -- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1".
@ -5671,7 +5728,13 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
end end
end end
local ctldzone = {} -- #CTLD.CargoZone local exists = true
local ctldzone = self:GetCTLDZone(Name, Type) -- #CTLD.CargoZone
if not ctldzone then
exists = false
ctldzone = {}
end
ctldzone.active = Active or false ctldzone.active = Active or false
ctldzone.color = Color or SMOKECOLOR.Red ctldzone.color = Color or SMOKECOLOR.Red
ctldzone.name = Name or "NONE" ctldzone.name = Name or "NONE"
@ -5698,10 +5761,55 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
ctldzone.shipwidth = Shipwidth or 10 ctldzone.shipwidth = Shipwidth or 10
end end
if not exists then
self:AddZone(ctldzone) self:AddZone(ctldzone)
end
return self return self
end end
--- User function - find #CTLD.CargoZone zone by name.
-- @param #CTLD self
-- @param #string Name Name of this zone.
-- @param #string Type Type of this zone, #CTLD.CargoZoneType
-- @return #CTLD.CargoZone self
function CTLD:GetCTLDZone(Name, Type)
if Type == CTLD.CargoZoneType.LOAD then
for _, z in pairs(self.pickupZones) do
if z.name == Name then
return z
end
end
elseif Type == CTLD.CargoZoneType.DROP then
for _, z in pairs(self.dropOffZones) do
if z.name == Name then
return z
end
end
elseif Type == CTLD.CargoZoneType.SHIP then
for _, z in pairs(self.shipZones) do
if z.name == Name then
return z
end
end
elseif Type == CTLD.CargoZoneType.BEACON then
for _, z in pairs(self.droppedBeacons) do
if z.name == Name then
return z
end
end
else
for _, z in pairs(self.wpZones) do
if z.name == Name then
return z
end
end
end
return nil
end
--- User function - Creates and adds a #CTLD.CargoZone zone for this CTLD instance from an Airbase or FARP name. --- User function - Creates and adds a #CTLD.CargoZone zone for this CTLD instance from an Airbase or FARP name.
-- Zones of type LOAD: Players load crates and troops here. -- Zones of type LOAD: Players load crates and troops here.
-- Zones of type DROP: Players can drop crates here. Note that troops can be unloaded anywhere. -- Zones of type DROP: Players can drop crates here. Note that troops can be unloaded anywhere.
@ -7443,9 +7551,12 @@ end
-- @return #CTLD self -- @return #CTLD self
function CTLD:onafterCratesBuild(From, Event, To, Group, Unit, Vehicle) function CTLD:onafterCratesBuild(From, Event, To, Group, Unit, Vehicle)
self:T({From, Event, To}) self:T({From, Event, To})
if self.movetroopstowpzone then if self.movetroopstowpzone and Vehicle then
local cg = self:GetGenericCargoObjectFromGroupName(Vehicle:GetName())
if not (cg and (cg.NoMoveToZone or (self.nomovetozone_names and self.nomovetozone_names[cg:GetName()]))) then
self:_MoveGroupToZone(Vehicle) self:_MoveGroupToZone(Vehicle)
end end
end
return self return self
end end

View File

@ -5029,7 +5029,7 @@ function FLIGHTGROUP:_UpdateMenu(delay)
-- Message to group. -- Message to group.
MESSAGE:New(text, 5):ToGroup(self.group) MESSAGE:New(text, 5):ToGroup(self.group)
self:I(self.lid..text) self:T(self.lid..text)
end end
-- Get current position of player. -- Get current position of player.

View File

@ -662,6 +662,15 @@ function LEGION:CheckMissionQueue()
if mission:IsNotOver() and mission:IsReadyToCancel() then if mission:IsNotOver() and mission:IsReadyToCancel() then
mission:Cancel() mission:Cancel()
end end
-- Housekeeping
local TNow = timer.getTime()
if mission:IsOver() and mission:IsNotRepeatable() and mission.DeletionTimstamp == nil then
mission.DeletionTimstamp = TNow
end
if mission.DeletionTimstamp ~= nil and TNow - mission.DeletionTimstamp > 1800 then
mission = nil
end
end end
-- Check that runway is operational and that carrier is not recovering. -- Check that runway is operational and that carrier is not recovering.
@ -761,7 +770,7 @@ function LEGION:CheckMissionQueue()
-- Reduce number of reinforcements. -- Reduce number of reinforcements.
if reinforce then if reinforce then
mission.reinforce=mission.reinforce-#assets mission.reinforce=mission.reinforce-#assets
self:I(self.lid..string.format("Reinforced with N=%d Nreinforce=%d", #assets, mission.reinforce)) self:T(self.lid..string.format("Reinforced with N=%d Nreinforce=%d", #assets, mission.reinforce))
end end
return true return true

View File

@ -4631,7 +4631,12 @@ function OPSGROUP:_UpdateTask(Task, Mission)
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName())) self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName()))
-- Task done. -- Task done.
if Task.StayInZoneTime then
local stay = Task.StayInZoneTime
self:__TaskDone(stay,Task)
else
self:TaskDone(Task) self:TaskDone(Task)
end
else else
-- Current zone NOT captured yet ==> Find Target -- Current zone NOT captured yet ==> Find Target
@ -7534,7 +7539,7 @@ end
function OPSGROUP:onafterElementDead(From, Event, To, Element) function OPSGROUP:onafterElementDead(From, Event, To, Element)
-- Debug info. -- Debug info.
self:I(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime())) self:T(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
-- Set element status. -- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
@ -8090,7 +8095,7 @@ function OPSGROUP:onafterStop(From, Event, To)
_DATABASE.FLIGHTGROUPS[self.groupname]=nil _DATABASE.FLIGHTGROUPS[self.groupname]=nil
-- Debug output. -- Debug output.
self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE") self:T(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
end end
--- On after "OutOfAmmo" event. --- On after "OutOfAmmo" event.

View File

@ -606,6 +606,7 @@ ENUMS.Storage = {
OH58 = {}, -- Kiowa specifics OH58 = {}, -- Kiowa specifics
UH1H = {}, -- Huey specifics UH1H = {}, -- Huey specifics
AH64D = {}, -- Huey specifics AH64D = {}, -- Huey specifics
UH60L = {}, -- Blackhawk specifics
} }
} }
@ -1315,6 +1316,26 @@ ENUMS.Storage.weapons.UH1H.M134_MiniGun_Right_Door = {4,15,46,175}
ENUMS.Storage.weapons.UH1H.M60_MG_Right_Door = {4,15,46,177} ENUMS.Storage.weapons.UH1H.M60_MG_Right_Door = {4,15,46,177}
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left_Door = {4,15,46,174} ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left_Door = {4,15,46,174}
ENUMS.Storage.weapons.UH1H.M60_MG_Left_Door = {4,15,46,176} ENUMS.Storage.weapons.UH1H.M60_MG_Left_Door = {4,15,46,176}
-- UH-60L
ENUMS.Storage.weapons.UH60L.M151_HYDRA = {4, 7, 33, 147} -- 2.75" Hydra, UnGd Rkts M151, HE
ENUMS.Storage.weapons.UH60L.M156_HYDRA = {4, 7, 33, 148} -- 2.75" Hydra, UnGd Rkts M156, Wht Phos
ENUMS.Storage.weapons.UH60L.M229_HYDRA = {4, 7, 33, 148} -- 2.75" Hydra, UnGd Rkts M229, HE
ENUMS.Storage.weapons.UH60L.M257_HYDRA = {4, 7, 33, 151} -- 2.75" Hydra, UnGd Rkts M257, Para Illum
ENUMS.Storage.weapons.UH60L.M259_HYDRA = {4, 7, 33, 151} -- 2.75" Hydra, UnGd Rkts M259, Smoke Marker
ENUMS.Storage.weapons.UH60L.M274_HYDRA = {4, 7, 33, 150} -- 2.75" Hydra, UnGd Rkts M274, Practice Smk
ENUMS.Storage.weapons.UH60L.M134_DOOR_GUN = {4, 15, 46, 3031}
ENUMS.Storage.weapons.UH60L.M3M = {4, 15, 46, 2496}
ENUMS.Storage.weapons.UH60L.M3M_DOOR_GUN = {4, 15, 46, 3032}
ENUMS.Storage.weapons.UH60L.M60_DOOR_GUN = {4, 15, 46, 3033}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_200 = {1,3,43,3023}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_230 = {1,3,43,3024}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_450 = {1,3,43,3025}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_DUAL_AUX = {1,3,43,3026}
ENUMS.Storage.weapons.UH60L.CARGO_SEAT_REAR_ROW = {1,3,43,3030}
ENUMS.Storage.weapons.UH60L.CARGO_SEAT_THREE_ROWS = {1,3,43,3029}
ENUMS.Storage.weapons.UH60L.EMPTY_GUNNER_SEAT_1 = {1,3,43,3027}
ENUMS.Storage.weapons.UH60L.EMPTY_GUNNER_SEAT_2 = {1,3,43,3028}
-- Kiowa -- Kiowa
ENUMS.Storage.weapons.OH58.FIM92 = {4,4,7,449} ENUMS.Storage.weapons.OH58.FIM92 = {4,4,7,449}
ENUMS.Storage.weapons.OH58.MG_M3P100 = {4,15,46,2611} ENUMS.Storage.weapons.OH58.MG_M3P100 = {4,15,46,2611}

View File

@ -2330,6 +2330,16 @@ function UTILS.IsLoadingDoorOpen( unit_name )
return true return true
end end
if type_name == "UH-60L_DAP" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
BASE:T(unit_name .. " cargo door is open")
return true
end
if type_name == "UH-60L_DAP" and (unit:getDrawArgumentValue(38) > 0 or unit:getDrawArgumentValue(400) == 1 ) then
BASE:T(unit_name .. " front door(s) are open")
return true
end
if type_name == "AH-64D_BLK_II" then if type_name == "AH-64D_BLK_II" then
BASE:T(unit_name .. " front door(s) are open") BASE:T(unit_name .. " front door(s) are open")
return true -- no doors on this one ;) return true -- no doors on this one ;)
@ -4127,6 +4137,45 @@ function UTILS.LCGRandom()
return UTILS.lcg.seed / UTILS.lcg.m return UTILS.lcg.seed / UTILS.lcg.m
end end
--- Create a table of grid-points for n points.
-- @param #number startVec2 Starting DCS#Vec2 map coordinate, e.g. `{x=63598575,y=-63598575}`
-- @param #number n Number of points to generate.
-- @param #number spacingX Horizonzal spacing (meters).
-- @param #number spacingY Vertical spacing (meters).
-- @return #table Grid Table of DCS#Vec2 entries.
function UTILS.GenerateGridPoints(startVec2, n, spacingX, spacingY)
local points = {}
local gridSize = math.ceil(math.sqrt(n))
local count = 0
local n = n or 1
local spacingX = spacingX or 100
local spacingY = spacingY or 100
local startX = startVec2.x or 100
local startY = startVec2.y or 100
for row = 0, gridSize - 1 do
for col = 0, gridSize - 1 do
if count >= n then
break
end
local point = {
x = startX + (col * spacingX),
y = startY + (row * spacingY)
}
table.insert(points, point)
count = count + 1
end
if count >= n then
break
end
end
return points
end
--- Spawns a new FARP of a defined type and coalition and functional statics (fuel depot, ammo storage, tent, windsock) around that FARP to make it operational. --- Spawns a new FARP of a defined type and coalition and functional statics (fuel depot, ammo storage, tent, windsock) around that FARP to make it operational.
-- Adds vehicles from template if given. Fills the FARP warehouse with liquids and known materiels. -- Adds vehicles from template if given. Fills the FARP warehouse with liquids and known materiels.
-- References: [DCS Forum Topic](https://forum.dcs.world/topic/282989-farp-equipment-to-run-it) -- References: [DCS Forum Topic](https://forum.dcs.world/topic/282989-farp-equipment-to-run-it)
@ -4147,10 +4196,38 @@ end
-- @param #string F10Text Text to display on F10 map if given. Handy to post things like the ADF beacon Frequency, Callsign and ATC Frequency. -- @param #string F10Text Text to display on F10 map if given. Handy to post things like the ADF beacon Frequency, Callsign and ATC Frequency.
-- @param #boolean DynamicSpawns If true, allow Dynamic Spawns from this FARP. -- @param #boolean DynamicSpawns If true, allow Dynamic Spawns from this FARP.
-- @param #boolean HotStart If true and DynamicSpawns is true, allow hot starts for Dynamic Spawns from this FARP. -- @param #boolean HotStart If true and DynamicSpawns is true, allow hot starts for Dynamic Spawns from this FARP.
-- @param #number NumberPads If given, spawn this number of pads.
-- @param #number SpacingX For NumberPads > 1, space this many meters horizontally. Defaults to 100.
-- @param #number SpacingY For NumberPads > 1, space this many meters vertically. Defaults to 100.
-- @return #list<Wrapper.Static#STATIC> Table of spawned objects and vehicle object (if given). -- @return #list<Wrapper.Static#STATIC> Table of spawned objects and vehicle object (if given).
-- @return #string ADFBeaconName Name of the ADF beacon, to be able to remove/stop it later. -- @return #string ADFBeaconName Name of the ADF beacon, to be able to remove/stop it later.
-- @return #number MarkerID ID of the F10 Text, to be able to remove it later. -- @return #number MarkerID ID of the F10 Text, to be able to remove it later.
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment,Airframes,F10Text,DynamicSpawns,HotStart) function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment,Airframes,F10Text,DynamicSpawns,HotStart,NumberPads,SpacingX,SpacingY)
local function PopulateStorage(Name,liquids,equip,airframes)
local newWH = STORAGE:New(Name)
if liquids and liquids > 0 then
-- Storage fill-up
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
end
if equip and equip > 0 then
for cat,nitem in pairs(ENUMS.Storage.weapons) do
for name,item in pairs(nitem) do
newWH:SetItem(item,equip)
end
end
end
if airframes and airframes > 0 then
for typename in pairs (CSAR.AircraftType) do
newWH:SetItem(typename,airframes)
end
end
end
-- Set Defaults -- Set Defaults
local farplocation = Coordinate local farplocation = Coordinate
@ -4171,12 +4248,36 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA) local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
local ReturnObjects = {} local ReturnObjects = {}
-- many FARPs
local NumberPads = NumberPads or 1
local SpacingX = SpacingX or 100
local SpacingY = SpacingY or 100
local FarpVec2 = Coordinate:GetVec2()
if NumberPads > 1 then
local Grid = UTILS.GenerateGridPoints(FarpVec2, NumberPads, SpacingX, SpacingY)
for id,gridpoint in ipairs(Grid) do
-- Spawn FARP
local location = COORDINATE:NewFromVec2(gridpoint)
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
local spawnedfarp = newfarp:SpawnFromCoordinate(location,0,Name.."-"..id)
table.insert(ReturnObjects,spawnedfarp)
PopulateStorage(Name.."-"..id,liquids,equip,airframes)
end
else
-- Spawn FARP -- Spawn FARP
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP" local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS" newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart) newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name) local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp) table.insert(ReturnObjects,spawnedfarp)
PopulateStorage(Name,liquids,equip,airframes)
end
-- Spawn Objects -- Spawn Objects
local FARPStaticObjectsNato = { local FARPStaticObjectsNato = {
["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"}, ["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"},
@ -4210,29 +4311,6 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
table.insert(ReturnObjects,spawnedvehicle) table.insert(ReturnObjects,spawnedvehicle)
end end
local newWH = STORAGE:New(Name)
if liquids and liquids > 0 then
-- Storage fill-up
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
end
if equip and equip > 0 then
for cat,nitem in pairs(ENUMS.Storage.weapons) do
for name,item in pairs(nitem) do
newWH:SetItem(item,equip)
end
end
end
if airframes and airframes > 0 then
for typename in pairs (CSAR.AircraftType) do
newWH:SetItem(typename,airframes)
end
end
local ADFName local ADFName
if ADF and type(ADF) == "number" then if ADF and type(ADF) == "number" then
local ADFFreq = ADF*1000 -- KHz to Hz local ADFFreq = ADF*1000 -- KHz to Hz

View File

@ -1574,6 +1574,17 @@ end
return self return self
end end
--- Get the true airbase center as seen in the ME. The position returned by the dcs object is is wrong and often at the start of the runway.
-- @return DCS#Vec2 The center of the true center of the airbase if it contains runways, otherwise the default DCS object position.
function AIRBASE:GetVec2()
local runways = self:GetRunways()
if runways and #runways > 0 then
return runways[1].center:GetVec2()
end
return self:GetCoordinate():GetVec2()
end
--- Get the category of this airbase. This is only a debug function because DCS 2.9 incorrectly returns heliports as airdromes. --- Get the category of this airbase. This is only a debug function because DCS 2.9 incorrectly returns heliports as airdromes.
-- @param #AIRBASE self -- @param #AIRBASE self
function AIRBASE:_GetCategory() function AIRBASE:_GetCategory()

View File

@ -4163,7 +4163,7 @@ function CONTROLLABLE:OptionRestrictBurner( RestrictBurner )
end end
--- Sets Controllable Option for A2A attack range for AIR FIGHTER units. --- [AIR] Sets Controllable Option for A2A attack range for AIR FIGHTER units.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number range Defines the range -- @param #number range Defines the range
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
@ -4188,6 +4188,66 @@ function CONTROLLABLE:OptionAAAttackRange( range )
return nil return nil
end end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA minimum firing height.
-- @param #CONTROLLABLE self
-- @param #number meters The minimum height in meters.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMinFiringHeightMeters(meters)
self:F2( { self.ControllableName } )
local meters = meters or 20
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsGround() then
self:SetOption(27, meters)
end
end
return self
end
return nil
end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA maximum firing height.
-- @param #CONTROLLABLE self
-- @param #number meters The maximum height in meters.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMaxFiringHeightMeters(meters)
self:F2( { self.ControllableName } )
local meters = meters or 1000
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsGround() then
self:SetOption(29, meters)
end
end
return self
end
return nil
end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA minimum firing height.
-- @param #CONTROLLABLE self
-- @param #number feet The minimum height in feet.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMinFiringHeightFeet(feet)
self:F2( { self.ControllableName } )
local feet = feet or 60
return self:OptionAAAMinFiringHeightMeters(UTILS.FeetToMeters(feet))
end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA maximum firing height.
-- @param #CONTROLLABLE self
-- @param #number feet The maximum height in feet.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMaxFiringHeightfeet(feet)
self:F2( { self.ControllableName } )
local feet = feet or 3000
return self:OptionAAAMaxFiringHeightMeters(UTILS.FeetToMeters(feet))
end
--- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically. --- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100. -- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100.

View File

@ -108,6 +108,8 @@ DYNAMICCARGO.State = {
-- @type DYNAMICCARGO.AircraftTypes -- @type DYNAMICCARGO.AircraftTypes
DYNAMICCARGO.AircraftTypes = { DYNAMICCARGO.AircraftTypes = {
["CH-47Fbl1"] = "CH-47Fbl1", ["CH-47Fbl1"] = "CH-47Fbl1",
["Mi-8MTV2"] = "CH-47Fbl1",
["Mi-8MT"] = "CH-47Fbl1",
} }
--- Helo types possible. --- Helo types possible.
@ -120,17 +122,30 @@ DYNAMICCARGO.AircraftDimensions = {
["length"] = 11, ["length"] = 11,
["ropelength"] = 30, ["ropelength"] = 30,
}, },
["Mi-8MTV2"] = {
["width"] = 6,
["height"] = 6,
["length"] = 15,
["ropelength"] = 30,
},
["Mi-8MT"] = {
["width"] = 6,
["height"] = 6,
["length"] = 15,
["ropelength"] = 30,
},
} }
--- DYNAMICCARGO class version. --- DYNAMICCARGO class version.
-- @field #string version -- @field #string version
DYNAMICCARGO.version="0.0.7" DYNAMICCARGO.version="0.0.9"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot... -- TODO: A lot...
-- DONE: Added Mi-8 type and dimensions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor

View File

@ -361,6 +361,7 @@ function POSITIONABLE:GetCoord()
-- Get the current position. -- Get the current position.
local PositionableVec3 = self:GetVec3() local PositionableVec3 = self:GetVec3()
if PositionableVec3 then
if self.coordinate then if self.coordinate then
-- Update COORDINATE from 3D vector. -- Update COORDINATE from 3D vector.
self.coordinate:UpdateFromVec3( PositionableVec3 ) self.coordinate:UpdateFromVec3( PositionableVec3 )
@ -371,6 +372,7 @@ function POSITIONABLE:GetCoord()
return self.coordinate return self.coordinate
end end
end
-- Error message. -- Error message.
BASE:E( { "Cannot GetCoordinate", Positionable = self, Alive = self:IsAlive() } ) BASE:E( { "Cannot GetCoordinate", Positionable = self, Alive = self:IsAlive() } )