Compare commits

...

94 Commits

Author SHA1 Message Date
Thomas
6058160160 Merge pull request #2426 from FlightControl-Master/Applevangelist-patch-3-1
Update Positionable.lua
2025-10-29 07:02:10 +01:00
Thomas
479dfc28b9 Update Positionable.lua 2025-10-29 07:01:33 +01:00
Applevangelist
8392788cdb Minor fixes 2025-10-26 16:19:10 +01:00
Thomas
55242edbde Merge pull request #2423 from nasgroup94/master
Minor Fixes
2025-10-26 07:27:37 +01:00
nasgroup94
2c5d9f043e fixed TIG bug causing NG Removed old into wind function 2025-10-25 12:42:09 -04:00
nasgroup94
2b2acbe244 Merge branch 'master' of https://github.com/nasgroup94/MOOSE 2025-10-25 12:39:54 -04:00
Applevangelist
7d4e103660 #UTILS - SpawnFarp helper, build multiple pads as a single static group, not separate objects. 2025-10-25 16:56:16 +02:00
Applevangelist
86798ae9ea #CTLD - fix 2025-10-24 16:27:17 +02:00
Thomas
2830e6d199 Merge pull request #2421 from leka1986/patch-1
Update CTLD.lua
2025-10-24 16:16:00 +02:00
leka1986
8c07573f8f Update CTLD.lua 2025-10-24 16:13:18 +02:00
leka1986
f3af0262df Update CTLD.lua
Added Crates amount to the menus and AddCratesCargoNoMove.
2025-10-24 14:02:06 +02:00
Applevangelist
0f270a6a35 #UTILS - FARP helper - add options to spawn multiple pads 2025-10-23 12:10:05 +02:00
Applevangelist
8432f46e48 #AIRBOSS - Fixed omission of magnetic in expected BRC call 2025-10-23 08:10:26 +02:00
nasgroup94
faccee88e7 added missing end to TIG "_OK_" subtraction 2025-10-22 05:19:50 -04:00
Thomas
e8246b3b90 Merge pull request #2420 from FlightControl-Master/revert-2417-master
Revert "Minor fixes"
2025-10-22 07:23:37 +02:00
Thomas
eb4e7b9281 Revert "Minor fixes" 2025-10-22 07:23:21 +02:00
Thomas
1e2190a6cc Merge pull request #2417 from nasgroup94/master
Minor fixes
2025-10-22 06:31:24 +02:00
nasgroup94
1644c1dc5b fixes to goshawk AOA f14 AA and TIG bug 2025-10-21 16:49:51 -04:00
nasgroup94
5fc3798c42 Update Airboss.lua
Fixed possible bug where a player will get points deducted when getting a perfect groove time
2025-10-21 05:44:19 -04:00
Applevangelist
fae81cdb1b Merge remote-tracking branch 'origin/master' 2025-10-13 18:51:59 +02:00
Applevangelist
dbae37f151 xx 2025-10-13 18:51:55 +02:00
Applevangelist
f7d58a0b76 #ZONE - Added missing ZONE_UNIT:UpdateFromUnit() and ZONE_GROUP:UpdateFromGroup() 2025-10-13 18:51:22 +02:00
Applevangelist
2e0d1fd90f #DYNAMICCARGO - Added Mi-8 type and dimensions 2025-10-12 19:14:59 +02:00
Applevangelist
69e6497655 #CTLD - Small fix for subcats 2025-10-12 17:23:54 +02:00
Thomas
ef8c7c9084 Merge pull request #2415 from shaji-Dev/master
[ADDED] UH-60L 'DAP' to `UTILS.IsLoadingDoorOpen` check
2025-10-10 11:41:52 +02:00
shaji-Dev
e4eaf72769 Merge branch 'FlightControl-Master:master' into master 2025-10-10 09:19:46 +02:00
smiki
5138ced630 Merge remote-tracking branch 'origin/master' 2025-10-10 09:19:31 +02:00
smiki
534f445f8c [ADDED] UH-60L 'DAP' to UTILS.IsLoadingDoorOpen check 2025-10-10 09:19:23 +02:00
Thomas
b2cc3e5329 Merge pull request #2413 from leka1986/master
Master
2025-10-09 17:41:07 +02:00
Thomas
efde321616 Merge pull request #2412 from shaji-Dev/master
[ADDED] Fuel Tanks and gunner seat loadout enums for UH-60L mod
2025-10-09 17:40:03 +02:00
leka1986
3a61581608 #Added a new option ( true by default), returntroopstobase. If set to false, troops would not return to base when dropped at load zone. 2025-10-09 17:33:15 +02:00
shaji-Dev
3a10f0b946 Merge branch 'FlightControl-Master:master' into master 2025-10-09 13:30:04 +02:00
smiki
6dc6972bdb [ADDED] Fuel Tanks and gunner seat loadout enums for UH-60L mod 2025-10-09 13:29:45 +02:00
Applevangelist
405235a59d #CONTROLLABLE - Fix for double brackets 2025-10-08 16:01:21 +02:00
Thomas
9c148625e4 Merge pull request #2410 from shaji-Dev/master
[FIXED] CTLD. Memory leak adding zones of the same name and type
2025-10-08 13:24:57 +02:00
shaji-Dev
a08d82a3d9 Merge branch 'FlightControl-Master:master' into master 2025-10-08 13:15:39 +02:00
smiki
48b51f21de [FIXED] CTLD. Memory leak adding zones of the same name and type
[FIXED] CSAR. nil pointer
2025-10-08 13:15:22 +02:00
Applevangelist
3260279cb7 xx 2025-10-08 12:29:59 +02:00
Applevangelist
1e60a0a32a #RAT 3.0.0 2025-10-07 18:06:20 +02:00
Applevangelist
146f869aaa #RAT - reduce log noise 2025-10-07 17:48:06 +02:00
Thomas
0bd35727f4 Merge pull request #2408 from shaji-Dev/master
[FIXED] Incorrect Airbase center position. [ADDED] UH-60L weapons
2025-10-07 15:51:44 +02:00
Thomas
5183fcc316 Update Enums.lua 2025-10-07 15:51:22 +02:00
shaji-Dev
bf7596521c Merge branch 'FlightControl-Master:master' into master 2025-10-07 15:42:17 +02:00
smiki
a85b6c960c [FIXED] Incorrect Airbase center position
[ADDED] UH-60L weapons
2025-10-07 15:41:54 +02:00
Thomas
a1aebf0575 Merge pull request #2406 from nasgroup94/master
AIRBOSS adjustments
2025-10-07 10:14:31 +02:00
nasgroup94
0f42218681 minor edits 2025-10-06 18:08:23 -04:00
frankiep95
5404f9ef19 Minor Fixes to grading fixed high low comments and BRC comment 2025-10-05 13:31:18 -04:00
frankiep95
8a84be19df Merge branch 'master' of https://github.com/nasgroup94/MOOSE 2025-10-05 12:50:58 -04:00
frankiep95
fd4ea81e46 minor fixes to grading, and BRC callout 2025-10-05 12:50:56 -04:00
Thomas
3df79aedb1 Merge pull request #2404 from shaji-Dev/master
[FIXED] SPAWNSTATIC not registering script spawned static templates therefore ReSpawn is not working.
2025-10-05 18:04:09 +02:00
smiki
4141aa35ba Merge remote-tracking branch 'origin/master' 2025-10-05 16:57:41 +02:00
smiki
6e45ee558e [FIXED] SPAWNSTATIC not registering script spawned static templates therefore ReSpawn is not working. 2025-10-05 16:57:33 +02:00
Applevangelist
db138be5f3 #SCoRING - suppress autocreation of CSV files better 2025-10-05 13:52:22 +02:00
Applevangelist
5ae6495e69 #CSAR Added functionality to determine if a landing took place at a helo base (named "H ..." in newer maps). 2025-10-05 13:51:55 +02:00
Thomas
58f1bc5531 Merge pull request #2402 from shaji-Dev/master
[ADDED] `SET_OPSGROUP:CountAlive`
2025-10-01 15:25:45 +02:00
smiki
935b52c489 [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 15:11:44 +02:00
smiki
aace98545a [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 15:07:18 +02:00
smiki
f39236c8fd [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 14:59:59 +02:00
smiki
43b4a6834b [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 14:50:04 +02:00
shaji-Dev
aa3978b04d Merge branch 'FlightControl-Master:master' into master 2025-09-30 21:17:51 +02:00
smiki
b51e758516 [ADDED] SET_OPSGROUP:CountAlive 2025-09-30 21:17:29 +02:00
Thomas
5be1832c09 Merge pull request #2400 from shaji-Dev/master
[ADDED] `UH-60L DAP to CSAR and CTLD`
2025-09-26 15:52:30 +02:00
smiki
362652ac6c [ADDED] CSAR. UH-60L DAP aircraft type 2025-09-26 15:14:12 +02:00
smiki
aec69884dc [ADDED] CTLD. UH-60L DAP default unit capability 2025-09-26 15:13:03 +02:00
Applevangelist
8fb4d4c7c6 #CTLD - add CratesPickedUp at one more point 2025-09-25 12:13:29 +02:00
Thomas
b5524b9a69 Merge pull request #2398 from shaji-Dev/master
[Fixed] Zone Scans returns objects outside the zone
2025-09-24 19:16:14 +02:00
smiki
ec0ff7afcd [Fixed] Zone Scans returns objects outside the zone 2025-09-24 15:14:21 +02:00
Thomas
d0cf68c2e2 Merge pull request #2396 from FlightControl-Master/Applevangelist-patch-3
Update Storage.lua
2025-09-23 15:26:54 +02:00
Thomas
3beb98a5e9 Update Storage.lua 2025-09-23 15:26:38 +02:00
Thomas
dd83ebe0e2 Merge pull request #2395 from FlightControl-Master/Applevangelist-patch-3
Fix for #Storage.lua some missing string concats
2025-09-23 15:19:42 +02:00
Thomas
6fdf3957bd Fix for #Storage.lua some missing string concats 2025-09-23 15:19:10 +02:00
Thomas
f3d586d455 Merge pull request #2393 from FlightControl-Master/Applevangelist-patch-2
Update Airbase.lua
2025-09-22 11:13:59 +02:00
Thomas
c2f0ce0fa2 Update Airbase.lua 2025-09-22 11:13:44 +02:00
Thomas
354490d149 Merge pull request #2391 from FlightControl-Master/Applevangelist-patch-2
Update Airbase.lua Iraq Airbases
2025-09-22 11:09:47 +02:00
Thomas
c5b0be5d21 Update Airbase.lua Iraq Airbases 2025-09-22 11:08:15 +02:00
Thomas
5e8676cf8a Merge pull request #2387 from shaji-Dev/master
LandingAfterEjection error
2025-09-18 06:47:01 +02:00
smiki
f4264cd149 Merge remote-tracking branch 'origin/master' 2025-09-17 20:19:54 +02:00
smiki
65d1c4187e [Fixed] attempt to index field 'Place' (a nil value) in LandingAfterEjection 2025-09-17 20:19:17 +02:00
Thomas
cda1432d04 Merge pull request #2383 from shaji-Dev/master
[FIXED] CTLD. Injected spawns not using `InitValidateAndRepositionGroundUnits`
2025-09-11 05:13:50 +02:00
smiki
09e5fca1a5 [FIXED] CTLD. Injected spawns not using InitValidateAndRepositionGroundUnits 2025-09-11 00:38:20 +02:00
Thomas
e38d73df8b Merge pull request #2381 from shaji-Dev/master
[ADDED] `GROUP:GetBoundingBox()`
2025-09-03 10:35:42 +02:00
smiki
7df90b2d30 [ADDED] GROUP:GetBoundingBox() since POSITIONABLE:GetBoundingBox() is only for units 2025-09-03 09:12:20 +02:00
Thomas
a917ee8f1e Merge pull request #2379 from shaji-Dev/master
[FIXED] `UTILS.HdgTo to accept both Vec2 or Vec3`
2025-09-01 10:57:48 +02:00
smiki
44f3c776eb [FIXED] UTILS.HdgTo to accept both Vec2 or Vec3 2025-09-01 09:30:05 +02:00
Applevangelist
873879ff79 #AIRBOSS - Slight tweak to EnableSRS() when no parameters are handed in. 2025-08-31 18:22:17 +02:00
Applevangelist
6c1907f7e0 GROUO correction in class self line 3205 2025-08-31 13:27:24 +02:00
Applevangelist
42ecdd3b14 #AIRBASE - Add Sinai Damascus Intl Airbase in the enumerator 2025-08-31 13:24:35 +02:00
Thomas
297164a0ee Merge pull request #2378 from shaji-Dev/master
[ADDED] `SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius)`
2025-08-28 19:58:19 +02:00
smiki
323f09b06c [ADDED] SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius) 2025-08-28 19:38:05 +02:00
smiki
e003b91bbe [ADDED] SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius) 2025-08-28 19:36:22 +02:00
Thomas
f1636fc5a9 Merge pull request #2375 from tspindler-cms/TeTeT/master
Add support for Vietnam War Vessels carriers to Airboss
2025-08-27 14:23:36 +02:00
TeTeT Nimitz
db053398d2 Add support for Vietnam War Vessels carriers to Airboss:
- CVA-31 Bon Homme Richard
    - Generic Essex with SCB-125 upgrade (angled Deck)
    - CVAN-65 Enterprise 1966
    - CVN-65 Enterprise modern
2025-08-27 13:12:50 +02:00
Thomas
417caf1b62 Merge pull request #2370 from shaji-Dev/master
[ADDED] `ValidateAndRepositionGroundUnits` to UNIT and GROUP respawns
2025-08-26 06:22:57 +02:00
smiki
cc76851614 [ADDED] ValidateAndRepositionGroundUnits to UNIT and GROUP respawns 2025-08-25 23:08:25 +02:00
21 changed files with 2845 additions and 2168 deletions

View File

@@ -577,13 +577,19 @@ do -- Zones and Pathlines
-- For a rectangular polygon drawing, we have the width (y) and height (x).
local w=objectData.width
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 points={}
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right
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 sinRot = math.sin(rotation)
local cosRot = math.cos(rotation)
local dx = h / 2
local dy = w / 2
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")

View File

@@ -1508,7 +1508,9 @@ function EVENT:onEvent( Event )
else
if Event.place:isExist() and Object.getCategory(Event.place) ~= Object.Category.SCENERY then
Event.Place=AIRBASE:Find(Event.place)
Event.PlaceName=Event.Place:GetName()
if Event.Place then
Event.PlaceName=Event.Place:GetName()
end
end
end
end

View File

@@ -1637,6 +1637,7 @@ do -- COORDINATE
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
RoutePoint.linkUnit = AirbaseID
RoutePoint.helipadId = AirbaseID
RoutePoint.airdromeId = airbase:IsAirdrome() and AirbaseID or nil
elseif AirbaseCategory == Airbase.Category.AIRDROME then
RoutePoint.airdromeId = AirbaseID
else

View File

@@ -7865,6 +7865,28 @@ do -- SET_OPSGROUP
return self
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.
-- @param #SET_OPSGROUP self
-- @param #string GroupName Name of the group.

View File

@@ -1054,6 +1054,7 @@ end
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
-- @param #SPAWN self
-- @param #boolean OnOff Enable/disable the feature.
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the units.
-- @param #number Spacing (Optional) Minimum spacing between units in meters. Default is 5% of the search radius or 5 meters, whichever is larger.

View File

@@ -378,6 +378,20 @@ function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
return self
end
--- Uses Disposition and other fallback logic to find a better and valid ground spawn position.
--- NOTE: This is not a spawn randomizer.
--- It will try to a find clear ground location avoiding trees, water, roads, runways, map scenery, other statics and other units in the area.
--- Uses the initial position if it's a valid location.
-- @param #SPAWNSTATIC self
-- @param #boolean OnOff Enable/disable the feature.
-- @param #number MaxRadius (Optional) Max radius to search for a valid ground location in meters. Default is 10 times the max radius of the static.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius)
self.ValidateAndRepositionStatic = OnOff
self.ValidateAndRepositionStaticMaxRadius = MaxRadius
return self
end
--- Allows to place a CallFunction hook when a new static spawns.
-- The provided method will be called when a new group is spawned, including its given parameters.
-- The first parameter of the SpawnFunction is the @{Wrapper.Static#STATIC} that was spawned.
@@ -544,6 +558,14 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- Add static to the game.
local Static=nil --DCS#StaticObject
if self.ValidateAndRepositionStatic then
local validPos = UTILS.ValidateAndRepositionStatic(CountryID, Template.category, Template.type, Template, Template.shape_name, self.ValidateAndRepositionStaticMaxRadius)
if validPos then
Template.x = validPos.x
Template.y = validPos.y
end
end
if self.InitFarp then
local TemplateGroup={}
@@ -611,16 +633,15 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
if self.StaticCopyFrom ~= nil then
mystatic.StaticCopyFrom = self.StaticCopyFrom
if not _DATABASE.Templates.Statics[Template.name] then
local TemplateGroup={}
TemplateGroup.units={}
TemplateGroup.units[1]=Template
TemplateGroup.x=Template.x
TemplateGroup.y=Template.y
TemplateGroup.name=Template.name
_DATABASE:_RegisterStaticTemplate( TemplateGroup, self.CoalitionID, self.CategoryID, CountryID )
end
end
local TemplateGroup={}
TemplateGroup.units={}
TemplateGroup.units[1]=Template
TemplateGroup.x=Template.x
TemplateGroup.y=Template.y
TemplateGroup.name=Template.name
_DATABASE:_RegisterStaticTemplate( TemplateGroup, self.CoalitionID, self.CategoryID, CountryID )
return mystatic
end

View File

@@ -1180,15 +1180,13 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
local function EvaluateZone( ZoneObject )
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
if ZoneObject then
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
-- Get object category.
local ObjectCategory = Object.getCategory(ZoneObject)
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
local CoalitionDCSUnit = ZoneObject:getCoalition()
local Include = false
if not UnitCategories then
-- Anything found is included.
@@ -1936,6 +1934,21 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset)
return self
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}.
-- @param #ZONE_UNIT self
@@ -2073,6 +2086,22 @@ function ZONE_GROUP:GetVec2()
return ZoneVec2
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}.
-- @param #ZONE_GROUP self
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
@@ -3308,14 +3337,12 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
local function EvaluateZone( ZoneObject )
if ZoneObject then
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
local ObjectCategory = Object.getCategory(ZoneObject)
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
local CoalitionDCSUnit = ZoneObject:getCoalition()
local Include = false
if not UnitCategories then
-- Anything found is included.
@@ -3347,7 +3374,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
end
-- trying with box search
if ObjectCategory == Object.Category.SCENERY and self:IsVec3InZone(ZoneObject:getPoint()) then
if ObjectCategory == Object.Category.SCENERY then
local SceneryType = ZoneObject:getTypeName()
local SceneryName = ZoneObject:getName()
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}

File diff suppressed because it is too large Load Diff

View File

@@ -320,8 +320,10 @@ function SCORING:New( GameName, SavePath, AutoSave )
-- Create the CSV file.
self.AutoSavePath = SavePath
self.AutoSave = AutoSave or true
self:OpenCSV( GameName )
self.AutoSave = (AutoSave == nil or AutoSave == true) and true or false
if self.AutoSavePath and self.AutoSave == true then
self:OpenCSV( GameName )
end
return self
@@ -1935,7 +1937,7 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
TargetUnitType = TargetUnitType 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.GameName .. '"' .. ',' ..
'"' .. self.RunTime .. '"' .. ',' ..

View File

@@ -21,7 +21,7 @@
-- * Multiple carrier support due to object oriented approach.
-- * Unlimited number of players.
-- * Persistence of player results (optional). LSO grading data is saved to csv file.
-- * Trap sheet (optional).
-- * Trap sheet (optional).
-- * Finite State Machine (FSM) implementation.
--
-- **Supported Carriers:**
@@ -39,6 +39,8 @@
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_\(LHA-6\)) (LHA-6)
-- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61)
-- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_\(L02\)) (L02)
-- * BONHOMMERICHARD [VWV Mod]
-- * ENTERPRISE66 [VWV Mod]
--
-- **Supported Aircraft:**
--
@@ -1317,6 +1319,10 @@ AIRBOSS.AircraftCarrier={
-- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module]
-- @field #string VINSON USS Carl Vinson (CVN-70) [Deprecated!]
-- @field #string ESSEX Essex class carrier (e.g. USS Yorktown (CV-10)) [Magnitude 3 Carrier Module]
-- @field #string BONHOMMERICHARD USS Bon Homme Richard carrier [VWV Mod]
-- @field #string ESSEXSCB125 Generic Essex class carrier with angled deck (SCB-125 upgrade) [VWV Mod]
-- @field #string ENTERPRISE66 USS Enterprise in the 1966 configuration [VWV Mod]
-- @field #string ENTERPRISEMODERN USS Enterprise in a modern configuration [Derived VWV Mod]
-- @field #string HERMES HMS Hermes (R12) [V/STOL Carrier]
-- @field #string INVINCIBLE HMS Invincible (R05) [V/STOL Carrier]
-- @field #string TARAWA USS Tarawa (LHA-1) [V/STOL Carrier]
@@ -1331,8 +1337,12 @@ AIRBOSS.CarrierType = {
TRUMAN = "CVN_75",
STENNIS = "Stennis",
FORRESTAL = "Forrestal",
ENTERPRISE66 = "USS Enterprise 1966",
ENTERPRISEMODERN = "cvn-65",
VINSON = "VINSON",
ESSEX = "Essex",
BONHOMMERICHARD = "USS Bon Homme Richard",
ESSEXSCB125 = "essex_scb125",
HERMES = "HERMES81",
INVINCIBLE = "hms_invincible",
TARAWA = "LHA_Tarawa",
@@ -1756,7 +1766,7 @@ AIRBOSS.MenuF10Root = nil
--- Airboss class version.
-- @field #string version
AIRBOSS.version = "1.4.1"
AIRBOSS.version = "1.4.2"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2019,11 +2029,19 @@ function AIRBOSS:New( carriername, alias )
self:_InitNimitz()
elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then
self:_InitForrestal()
elseif self.carriertype == AIRBOSS.CarrierType.ENTERPRISE66 then
self:_InitEnterprise()
elseif self.carriertype == AIRBOSS.CarrierType.ENTERPRISEMODERN then
self:_InitEnterprise()
elseif self.carriertype == AIRBOSS.CarrierType.VINSON then
-- Carl Vinson is legacy now.
self:_InitStennis()
elseif self.carriertype == AIRBOSS.CarrierType.ESSEX then
self:_InitEssex()
elseif self.carriertype == AIRBOSS.CarrierType.BONHOMMERICHARD then
self:_InitBonHommeRichard()
elseif self.carriertype == AIRBOSS.CarrierType.ESSEXSCB125 then
self:_InitEssexSCB125()
elseif self.carriertype == AIRBOSS.CarrierType.HERMES then
-- Hermes parameters.
self:_InitHermes()
@@ -3110,8 +3128,8 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
self.SRS:SetCulture(Culture or "en-US")
--self.SRS:SetFrequencies(Frequencies)
self.SRS:SetGender(Gender or "male")
self.SRS:SetPath(PathToSRS)
self.SRS:SetPort(Port or 5002)
--self.SRS:SetPath(PathToSRS)
self.SRS:SetPort(Port or MSRS.port or 5002)
self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS")
self.SRS:SetCoordinate(self.carrier:GetCoordinate())
self.SRS:SetVolume(Volume or 1)
@@ -3122,7 +3140,10 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
if Voice then
self.SRS:SetVoice(Voice)
end
self.SRS:SetVolume(Volume or 1.0)
if (not Voice) and self.SRS and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then
self.SRS.voice = MSRS.poptions["gcloud"].voice or MSRS.Voices.Google.Standard.en_US_Standard_B
end
--self.SRS:SetVolume(Volume or 1.0)
-- SRSQUEUE
self.SRSQ = MSRSQUEUE:New("AIRBOSS")
self.SRSQ:SetTransmitOnlyWithPlayers(true)
@@ -4653,6 +4674,26 @@ function AIRBOSS:_InitForrestal()
end
--- Init parameters for Enterprise carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitEnterprise()
-- Using Forrestal as template
self:_InitForrestal()
self.carrierparam.sterndist = -164.30
self.carrierparam.deckheight = 19.52
self.carrierparam.totlength = 335
self.carrierparam.rwylength = 223
-- Wires.
self.carrierparam.wire1 = 57.7
self.carrierparam.wire2 = 69.6
self.carrierparam.wire3 = 79.5
self.carrierparam.wire4 = 90.0
end
--- Init parameters for Essec class carriers.
-- @param #AIRBOSS self
function AIRBOSS:_InitEssex()
@@ -4698,6 +4739,35 @@ function AIRBOSS:_InitEssex()
end
--- Init parameters for CVA-31 Bon Homme Richard carriers.
-- @param #AIRBOSS self
function AIRBOSS:_InitBonHommeRichard()
-- Init Essex as default
self:_InitEssex()
self.carrierparam.deckheight = 16.95
-- Landing runway.
-- from BHR EssexRunwayAndRoutes.lua
self.carrierparam.rwyangle = -11.4
self.carrierparam.rwylength = 97
self.carrierparam.rwywidth = 20
-- Wires.
self.carrierparam.wire1 = 40.4 -- Distance from stern to first wire. Original from Frank - 42
self.carrierparam.wire2 = 45
self.carrierparam.wire3 = 51
self.carrierparam.wire4 = 58.1
end
--- Init parameters for Generic Essex SC125 class carriers.
-- @param #AIRBOSS self
function AIRBOSS:_InitEssexSCB125()
-- Init Bon Homme Richard as default
self:_InitBonHommeRichard()
end
--- Init parameters for R12 HMS Hermes carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitHermes()
@@ -6880,6 +6950,9 @@ function AIRBOSS:_AddMarshalGroup( flight, stack )
-- Convert to clock string.
local Ccharlie = UTILS.SecondsToClock( flight.Tcharlie )
-- Make sure brc is never above 360
brc = brc % 360
-- Combined marshal call.
self:_MarshalCallArrived( flight.onboard, flight.case, brc, alt, Ccharlie, P )
@@ -7533,7 +7606,7 @@ function AIRBOSS:_InitPlayer( playerData, step )
playerData.landed = false
playerData.Tlso = timer.getTime()
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.flag = -100
playerData.debriefschedulerID = nil
@@ -8072,8 +8145,7 @@ end
--- Check current player status.
-- @param #AIRBOSS self
function AIRBOSS:_CheckPlayerStatus()
-- Loop over all players.
-- Loop over all players.
for _playerName, _playerData in pairs( self.players ) do
local playerData = _playerData -- #AIRBOSS.PlayerData
@@ -8345,7 +8417,7 @@ end
function AIRBOSS:_SetTimeInGroove( playerData )
-- Set time in the groove
if playerData.TIG0 then
if playerData.TIG0 then
playerData.Tgroove = timer.getTime() - playerData.TIG0 - 1.5 -- VNAO Edit - Subtracting an extra 1.5
else
playerData.Tgroove = 999
@@ -9574,7 +9646,7 @@ end
--- Break entry for case I/II recoveries.
-- @param #AIRBOSS self
-- @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)
local X, Z = self:_GetDistances( playerData.unit )
@@ -9585,111 +9657,16 @@ function AIRBOSS:_BreakEntry( playerData ) --Adam Edits begin 7/24/23
return
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).
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.
self:_PlayerHint( playerData )
-- Next step: Early Break.
self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.EARLYBREAK )
clientSHBFlag = false
end
end--Adam Edits end 7/24/23
end
--- Break.
-- @param #AIRBOSS self
@@ -10291,19 +10268,19 @@ function AIRBOSS:_Groove( playerData )
if rho >= RAR and rho <= RIM then
if gd.LUE > 0.22 and lineupError < -0.22 then
env.info " Drift Right across centre ==> DR-"
gd.Drift = " DR"
gd.Drift = "DR"
self:T( self.lid .. string.format( "Got Drift Right across centre step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError ) )
elseif gd.LUE < -0.22 and lineupError > 0.22 then
env.info " Drift Left ==> DL-"
gd.Drift = " DL"
gd.Drift = "DL"
self:T( self.lid .. string.format( "Got Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError ) )
elseif gd.LUE > 0.13 and lineupError < -0.14 then
env.info " Little Drift Right across centre ==> (DR-)"
gd.Drift = " (DR)"
gd.Drift = "(DR)"
self:T( self.lid .. string.format( "Got Little Drift Right across centre at step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError ) )
elseif gd.LUE < -0.13 and lineupError > 0.14 then
env.info " Little Drift Left across centre ==> (DL-)"
gd.Drift = " (DL)"
gd.Drift = "(DL)"
self:E( self.lid .. string.format( "Got Little Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError ) )
end
end
@@ -11981,10 +11958,12 @@ function AIRBOSS:GetHeading( magnetic )
hdg = hdg - self.magvar
end
-- Adjust negative values.
if hdg < 0 then
hdg = hdg + 360
end
-- -- Adjust negative values.
-- if hdg < 0 then
-- hdg = hdg + 360
-- end
hdg = hdg % 360 -- using this to replace the above function to prevent negative values and BRC higher than 360
return hdg
end
@@ -12250,8 +12229,8 @@ function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord )
local magvar= magnetic and self.magvar or 0
-- 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 = self:GetHeadingIntoWind_old(vdeck) -- VNAO Edit: Using old heading into wind algorithm
local intowind = (540 + (windto - magvar + math.deg(theta) )) % 360
return intowind, v
end
@@ -12681,7 +12660,7 @@ function AIRBOSS:_LSOgrade( playerData )
local TIG = ""
-- Analyse flight data and convert to LSO text.
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
local GXX, nXX = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.XX )
local GIM, nIM = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.IM )
@@ -12703,7 +12682,8 @@ function AIRBOSS:_LSOgrade( playerData )
local nL=count(G, '_')/2
local nS=count(G, '%(')
local nN=N-nS-nL
if TIG=="_OK_" then nL = nL -1 end --Circuit added to prevent grade deduction for perfect groove
-- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn.
local Tgroove=playerData.Tgroove
@@ -12733,7 +12713,6 @@ function AIRBOSS:_LSOgrade( playerData )
else
if vtol then
-- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe.--Pene testing
-- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation.
@@ -12875,16 +12854,21 @@ function AIRBOSS:_LSOgrade( playerData )
end
-- 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 points == 4 then -- VNAO EDIT: added
points = 3 -- VNAO EDIT: added
grade = "(OK)" -- VNAO EDIT: added
elseif points == 3 then -- VNAO EDIT: added
points = 2 -- VNAO EDIT: added
grade = "--" -- VNAO EDIT: added
end -- VNAO EDIT: added
end -- VNAO EDIT: added
-- -- 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 points == 4 then -- VNAO EDIT: added
-- points = 3 -- VNAO EDIT: added
-- grade = "(OK)" -- VNAO EDIT: added
-- elseif points == 3 then -- VNAO EDIT: added
-- points = 2 -- VNAO EDIT: added
-- grade = "--" -- 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)
@@ -12960,6 +12944,7 @@ function AIRBOSS:_Flightdata2Text( playerData, groovestep )
-- Speed via AoA. Depends on aircraft type.
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 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
@@ -12980,7 +12965,7 @@ function AIRBOSS:_Flightdata2Text( playerData, groovestep )
end
-- Glideslope/altitude. Good [-0.3, 0.4] asymmetric!
local A = nil
if GSE > self.gle.HIGH then
A = underline( "H" )
elseif GSE > self.gle.High then

View File

@@ -31,7 +31,7 @@
-- @image OPS_CSAR.jpg
---
-- Last Update July 2025
-- Last Update Oct 2025
-------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@@ -305,6 +305,7 @@ CSAR.AircraftType["Mi-24P"] = 8
CSAR.AircraftType["Mi-24V"] = 8
CSAR.AircraftType["Bell-47"] = 2
CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["UH-60L_DAP"] = 2
CSAR.AircraftType["AH-64D_BLK_II"] = 2
CSAR.AircraftType["Bronco-OV-10A"] = 2
CSAR.AircraftType["MH-60R"] = 10
@@ -314,7 +315,7 @@ CSAR.AircraftType["CH-47Fbl1"] = 31
--- CSAR class version.
-- @field #string version
CSAR.version="1.0.33"
CSAR.version="1.0.34"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -1149,11 +1150,11 @@ function CSAR:_EventHandler(EventData)
local initdcscoord = nil
local initcoord = nil
if _event.id == EVENTS.Ejection then
if _event.id == EVENTS.Ejection and _event.TgtDCSUnit then
initdcscoord = _event.TgtDCSUnit:getPoint()
initcoord = COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord})
else
elseif _event.IniDCSUnit then
initdcscoord = _event.IniDCSUnit:getPoint()
initcoord = COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord})
@@ -1243,7 +1244,10 @@ function CSAR:_EventHandler(EventData)
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
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
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
end
@@ -1730,8 +1734,9 @@ end
-- @param #string heliname Heli name
-- @param #string groupname Group name
-- @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)
function CSAR:_ScheduledSARFlight(heliname,groupname, isairport, noreschedule)
-- @param #boolean noreschedule If true, do not try to reschedule this if distances are not ok (coming from landing event)
-- @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({heliname,groupname})
local _heliUnit = self:_GetSARHeli(heliname)
@@ -1757,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))
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")
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true, true)
@@ -1772,7 +1777,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname, isairport, noreschedule)
--queue up
if not noreschedule then
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
return self
end

View File

@@ -25,7 +25,7 @@
-- @module Ops.CTLD
-- @image OPS_CTLD.jpg
-- Last Update July 2025
-- Last Update Oct 2025
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -147,6 +147,7 @@ CTLD_CARGO = {
Location = ZONE:New(Location)
end
self.Location = Location
self.NoMoveToZone = false
return self
end
@@ -783,6 +784,7 @@ do
-- 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".
-- 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:
-- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4)
@@ -1396,6 +1398,7 @@ CTLD.UnitTypeCapabilities = {
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers.
--Actually it's longer, but the center coord is off-center of the model.
["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["UH-60L_DAP"] = {type="UH-60L_DAP", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 16, cargoweightlimit = 500}, -- UH-60L DAP is an attack helo but can do limited CSAR and CTLD
["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
@@ -1417,7 +1420,7 @@ CTLD.FixedWingTypes = {
--- CTLD class version.
-- @field #string version
CTLD.version="1.3.37"
CTLD.version="1.3.38"
--- Instantiate a new CTLD.
-- @param #CTLD self
@@ -1555,6 +1558,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
self.smokedistance = 2000
self.movetroopstowpzone = true
self.movetroopsdistance = 5000
self.returntroopstobase = true -- if set to false, troops would stay after deployment inside a load zone.
self.troopdropzoneradius = 100
self.VehicleMoveFormation = AI.Task.VehicleFormation.VEE
@@ -3328,6 +3332,7 @@ function CTLD:_LoadCratesNearby(Group, Unit)
self:_RefreshLoadCratesMenu(Group, Unit)
-- clean up real world crates
self:_CleanupTrackedCrates(crateidsloaded)
self:__CratesPickedUp(1, Group, Unit, loaded.Cargo)
end
end
return self
@@ -3674,7 +3679,7 @@ function CTLD:_UnloadTroops(Group, Unit)
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase = true
droppingatbase = self.returntroopstobase
end
-- check for hover unload
local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters
@@ -4511,7 +4516,8 @@ function CTLD:_RefreshF10Menus()
end
for _,cargoObj in pairs(self.Cargo_Crates) do
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
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4522,7 +4528,8 @@ function CTLD:_RefreshF10Menus()
end
for _,cargoObj in pairs(self.Cargo_Statics) do
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
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4534,7 +4541,8 @@ function CTLD:_RefreshF10Menus()
else
for _,cargoObj in pairs(self.Cargo_Crates) do
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
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4545,7 +4553,8 @@ function CTLD:_RefreshF10Menus()
end
for _,cargoObj in pairs(self.Cargo_Statics) do
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
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4557,14 +4566,15 @@ function CTLD:_RefreshF10Menus()
end
end
else
if self.usesubcats then
if self.usesubcats == true then
local subcatmenus = {}
for catName, _ in pairs(self.subcats) do
subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case
end
for _, cargoObj in pairs(self.Cargo_Crates) do
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
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4573,7 +4583,8 @@ function CTLD:_RefreshF10Menus()
end
for _, cargoObj in pairs(self.Cargo_Statics) do
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
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4583,7 +4594,8 @@ function CTLD:_RefreshF10Menus()
else
for _, cargoObj in pairs(self.Cargo_Crates) do
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
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4592,7 +4604,8 @@ function CTLD:_RefreshF10Menus()
end
for _, cargoObj in pairs(self.Cargo_Statics) do
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
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -5132,7 +5145,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase = true
droppingatbase = self.returntroopstobase
end
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
@@ -5424,6 +5437,52 @@ function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,Sub
return self
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.
-- @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".
@@ -5668,8 +5727,14 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
return self
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.color = Color or SMOKECOLOR.Red
ctldzone.name = Name or "NONE"
@@ -5695,11 +5760,56 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
ctldzone.shiplength = Shiplength or 100
ctldzone.shipwidth = Shipwidth or 10
end
self:AddZone(ctldzone)
if not exists then
self:AddZone(ctldzone)
end
return self
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.
-- 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.
@@ -6930,6 +7040,7 @@ end
local alias = string.format("%s-%d", _template, math.random(1,100000))
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(randompositions,20,2)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:InitDelayOff()
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord)
@@ -7083,12 +7194,14 @@ end
if canmove then
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(true,20,2)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:InitDelayOff()
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord)
else -- don't random position of e.g. SAM units build as FOB
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitDelayOff()
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord)
end
@@ -7438,8 +7551,11 @@ end
-- @return #CTLD self
function CTLD:onafterCratesBuild(From, Event, To, Group, Unit, Vehicle)
self:T({From, Event, To})
if self.movetroopstowpzone then
self:_MoveGroupToZone(Vehicle)
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)
end
end
return self
end

File diff suppressed because it is too large Load Diff

View File

@@ -1586,12 +1586,12 @@ function UTILS.HdgDiff(h1, h2)
return math.abs(delta)
end
--- Returns the heading from one vec3 to another vec3.
-- @param DCS#Vec3 a From vec3.
-- @param DCS#Vec3 b To vec3.
--- Returns the heading from one vec2/vec3 to another vec2/vec3.
-- @param DCS#Vec3 a From Vec2 or Vec3.
-- @param DCS#Vec3 b To Vec2 or Vec3.
-- @return #number Heading in degrees.
function UTILS.HdgTo(a, b)
local dz=b.z-a.z
local dz=(b.z or b.y) - (a.z or a.y)
local dx=b.x-a.x
local heading=math.deg(math.atan2(dz, dx))
if heading < 0 then
@@ -2330,6 +2330,16 @@ function UTILS.IsLoadingDoorOpen( unit_name )
return true
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
BASE:T(unit_name .. " front door(s) are open")
return true -- no doors on this one ;)
@@ -4127,6 +4137,45 @@ function UTILS.LCGRandom()
return UTILS.lcg.seed / UTILS.lcg.m
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.
-- 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)
@@ -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 #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 #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 #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.
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
local farplocation = Coordinate
@@ -4171,12 +4248,74 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
local ReturnObjects = {}
-- Spawn FARP
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(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp)
-- 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)
local groupData = {
["visible"] = true,
["hidden"] = false,
["units"] = {},
["y"] = 0, -- Group center latitude
["x"] = 0, -- Group center longitude
["name"] = Name,
}
local unitData = {
["category"] = "Heliports",
["type"] = STypeName, -- FARP type
["y"] = 0, -- Latitude coordinate (meters)
["x"] = 0, -- Longitude coordinate (meters)
["name"] = Name,
["heading"] = 0, -- Heading in radians
["heliport_modulation"] = mod, -- 0 = AM, 1 = FM
["heliport_frequency"] = freq, -- Radio frequency in MHz
["heliport_callsign_id"] = callsign, -- Callsign ID
["dead"] = false,
["shape_name"] = SShapeName,
["dynamicSpawn"] = DynamicSpawns,
["allowHotStart"] = HotStart,
}
for id,gridpoint in ipairs(Grid) do
-- Spawn FARP
local UnitTemplate = UTILS.DeepCopy(unitData)
UnitTemplate.x = gridpoint.x
UnitTemplate.y = gridpoint.y
if id > 1 then UnitTemplate.name = Name.."-"..id end
table.insert(groupData.units,UnitTemplate)
if id==1 then
groupData.x = gridpoint.x
groupData.y = gridpoint.y
end
end
--BASE:I("Spawning FARP")
--UTILS.PrintTableToLog(groupData,1)
local Static=coalition.addGroup(Country, -1, groupData)
-- Currently DCS >= 2.8 does not trigger birth events if FARPS are spawned!
-- We create such an event. The airbase is registered in Core.Event
local Event = {
id = EVENTS.Birth,
time = timer.getTime(),
initiator = Static
}
-- Create BIRTH event.
world.onEvent(Event)
PopulateStorage(Name.."-1",liquids,equip,airframes)
else
-- Spawn FARP
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(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp)
PopulateStorage(Name,liquids,equip,airframes)
end
-- Spawn Objects
local FARPStaticObjectsNato = {
["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"},
@@ -4210,29 +4349,6 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
table.insert(ReturnObjects,spawnedvehicle)
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
if ADF and type(ADF) == "number" then
local ADFFreq = ADF*1000 -- KHz to Hz
@@ -4981,3 +5097,51 @@ function UTILS.ValidateAndRepositionGroundUnits(Positions, Anchor, MaxRadius, Sp
end
end
end
--- This function uses Disposition and other fallback logic to find better ground positions for statics.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
-- @param #table Positions A table of DCS#Vec2 or DCS#Vec3, can be a units table from the group template.
-- @param DCS#Vec2 Position DCS#Vec2 or DCS#Vec3 initial spawn location.
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the static.
-- @return DCS#Vec2 Initial Position if it's valid, else a valid spawn position. nil if no valid position found.
function UTILS.ValidateAndRepositionStatic(Country, Category, Type, Position, ShapeName, MaxRadius)
local coord = COORDINATE:NewFromVec2(Position)
local st = SPAWNSTATIC:NewFromType(Type, Category, Country)
if ShapeName then
st:InitShape(ShapeName)
end
local sName = "s-"..timer.getTime().."-"..math.random(1,10000)
local tempStatic = st:SpawnFromCoordinate(coord, 0, sName)
if tempStatic then
local sRadius = tempStatic:GetBoundingRadius(2) or 3
tempStatic:Destroy()
sRadius = sRadius * 0.5
MaxRadius = MaxRadius or math.max(sRadius * 10, 100)
local positions = UTILS.GetSimpleZones(coord:GetVec3(), MaxRadius, sRadius, 20)
if positions and #positions > 0 then
local closestSpot
local closestDist = math.huge
for _, spot in pairs(positions) do -- Disposition sometimes returns points on roads, hence this filter.
if land.getSurfaceType(spot) == land.SurfaceType.LAND then
local dist = UTILS.VecDist2D(Position, spot)
if dist < closestDist then
closestDist = dist
closestSpot = spot
end
end
end
if closestSpot then
if closestDist >= sRadius then
return closestSpot
else
return Position
end
end
end
end
return nil
end

View File

@@ -703,55 +703,56 @@ AIRBASE.SouthAtlantic={
--- Airbases of the Sinai map:
--
-- * AIRBASE.Sinai.Abu_Rudeis
-- * AIRBASE.Sinai.Abu_Suwayr
-- * AIRBASE.Sinai.Al_Bahr_al_Ahmar
-- * AIRBASE.Sinai.Al_Ismailiyah
-- * AIRBASE.Sinai.Al_Khatatbah
-- * AIRBASE.Sinai.Al_Mansurah
-- * AIRBASE.Sinai.Al_Rahmaniyah_Air_Base
-- * AIRBASE.Sinai.As_Salihiyah
-- * AIRBASE.Sinai.AzZaqaziq
-- * AIRBASE.Sinai.Baluza
-- * AIRBASE.Sinai.Ben_Gurion
-- * AIRBASE.Sinai.Beni_Suef
-- * AIRBASE.Sinai.Bilbeis_Air_Base
-- * AIRBASE.Sinai.Bir_Hasanah
-- * AIRBASE.Sinai.Birma_Air_Base
-- * AIRBASE.Sinai.Borg_El_Arab_International_Airport
-- * AIRBASE.Sinai.Cairo_International_Airport
-- * AIRBASE.Sinai.Cairo_West
-- * AIRBASE.Sinai.Difarsuwar_Airfield
-- * AIRBASE.Sinai.El_Arish
-- * AIRBASE.Sinai.El_Gora
-- * AIRBASE.Sinai.El_Minya
-- * AIRBASE.Sinai.Fayed
-- * AIRBASE.Sinai.Gebel_El_Basur_Air_Base
-- * AIRBASE.Sinai.Hatzerim
-- * AIRBASE.Sinai.Hatzor
-- * AIRBASE.Sinai.Hurghada_International_Airport
-- * AIRBASE.Sinai.Inshas_Airbase
-- * AIRBASE.Sinai.Jiyanklis_Air_Base
-- * AIRBASE.Sinai.Kedem
-- * AIRBASE.Sinai.Kibrit_Air_Base
-- * AIRBASE.Sinai.Kom_Awshim
-- * AIRBASE.Sinai.Melez
-- * AIRBASE.Sinai.Mezzeh_Air_Base
-- * AIRBASE.Sinai.Nevatim
-- * AIRBASE.Sinai.Ovda
-- * AIRBASE.Sinai.Palmachim
-- * AIRBASE.Sinai.Quwaysina
-- * AIRBASE.Sinai.Rafic_Hariri_Intl
-- * AIRBASE.Sinai.Ramat_David
-- * AIRBASE.Sinai.Ramon_Airbase
-- * AIRBASE.Sinai.Ramon_International_Airport
-- * AIRBASE.Sinai.Sde_Dov
-- * AIRBASE.Sinai.Sharm_El_Sheikh_International_Airport
-- * AIRBASE.Sinai.St_Catherine
-- * AIRBASE.Sinai.Tabuk
-- * AIRBASE.Sinai.Tel_Nof
-- * AIRBASE.Sinai.Wadi_Abu_Rish
-- * AIRBASE.Sinai.Wadi_al_Jandali
-- * AIRBASE.SinaiMap.Abu_Rudeis
-- * AIRBASE.SinaiMap.Abu_Suwayr
-- * AIRBASE.SinaiMap.Al_Bahr_al_Ahmar
-- * AIRBASE.SinaiMap.Al_Ismailiyah
-- * AIRBASE.SinaiMap.Al_Khatatbah
-- * AIRBASE.SinaiMap.Al_Mansurah
-- * AIRBASE.SinaiMap.Al_Rahmaniyah_Air_Base
-- * AIRBASE.SinaiMap.As_Salihiyah
-- * AIRBASE.SinaiMap.AzZaqaziq
-- * AIRBASE.SinaiMap.Baluza
-- * AIRBASE.SinaiMap.Ben_Gurion
-- * AIRBASE.SinaiMap.Beni_Suef
-- * AIRBASE.SinaiMap.Bilbeis_Air_Base
-- * AIRBASE.SinaiMap.Bir_Hasanah
-- * AIRBASE.SinaiMap.Birma_Air_Base
-- * AIRBASE.SinaiMap.Borg_El_Arab_International_Airport
-- * AIRBASE.SinaiMap.Cairo_International_Airport
-- * AIRBASE.SinaiMap.Cairo_West
-- * AIRBASE.SinaiMap.Damascus_Intl
-- * AIRBASE.SinaiMap.Difarsuwar_Airfield
-- * AIRBASE.SinaiMap.El_Arish
-- * AIRBASE.SinaiMap.El_Gora
-- * AIRBASE.SinaiMap.El_Minya
-- * AIRBASE.SinaiMap.Fayed
-- * AIRBASE.SinaiMap.Gebel_El_Basur_Air_Base
-- * AIRBASE.SinaiMap.Hatzerim
-- * AIRBASE.SinaiMap.Hatzor
-- * AIRBASE.SinaiMap.Hurghada_International_Airport
-- * AIRBASE.SinaiMap.Inshas_Airbase
-- * AIRBASE.SinaiMap.Jiyanklis_Air_Base
-- * AIRBASE.SinaiMap.Kedem
-- * AIRBASE.SinaiMap.Kibrit_Air_Base
-- * AIRBASE.SinaiMap.Kom_Awshim
-- * AIRBASE.SinaiMap.Melez
-- * AIRBASE.SinaiMap.Mezzeh_Air_Base
-- * AIRBASE.SinaiMap.Nevatim
-- * AIRBASE.SinaiMap.Ovda
-- * AIRBASE.SinaiMap.Palmachim
-- * AIRBASE.SinaiMap.Quwaysina
-- * AIRBASE.SinaiMap.Rafic_Hariri_Intl
-- * AIRBASE.SinaiMap.Ramat_David
-- * AIRBASE.SinaiMap.Ramon_Airbase
-- * AIRBASE.SinaiMap.Ramon_International_Airport
-- * AIRBASE.SinaiMap.Sde_Dov
-- * AIRBASE.SinaiMap.Sharm_El_Sheikh_International_Airport
-- * AIRBASE.SinaiMap.St_Catherine
-- * AIRBASE.SinaiMap.Tabuk
-- * AIRBASE.SinaiMap.Tel_Nof
-- * AIRBASE.SinaiMap.Wadi_Abu_Rish
-- * AIRBASE.SinaiMap.Wadi_al_Jandali
--
-- @field Sinai
AIRBASE.Sinai = {
@@ -773,6 +774,7 @@ AIRBASE.Sinai = {
["Borg_El_Arab_International_Airport"] = "Borg El Arab International Airport",
["Cairo_International_Airport"] = "Cairo International Airport",
["Cairo_West"] = "Cairo West",
["Damascus_Intl"] = "Damascus Intl",
["Difarsuwar_Airfield"] = "Difarsuwar Airfield",
["El_Arish"] = "El Arish",
["El_Gora"] = "El Gora",
@@ -931,37 +933,51 @@ AIRBASE.Afghanistan = {
--- Airbases of the Iraq map
--
-- * AIRBASE.Iraq.Baghdad_International_Airport
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
-- * AIRBASE.Iraq.Al_Sahra_Airport
-- * AIRBASE.Iraq.Erbil_International_Airpor
-- * AIRBASE.Iraq.Al_Taji_Airport
-- * AIRBASE.Iraq.Al_Asad_Airbase
-- * AIRBASE.Iraq.Al_Kut_Airbase
-- * AIRBASE.Iraq.Al_Sahra_Airport
-- * AIRBASE.Iraq.Al_Salam_Airbase
-- * AIRBASE.Iraq.Balad_Airbase
-- * AIRBASE.Iraq.Kirkuk_International_Airport
-- * AIRBASE.Iraq.Bashur_Airport
-- * AIRBASE.Iraq.Al_Taji_Airport
-- * AIRBASE.Iraq.Al_Taquddum_Airport
-- * AIRBASE.Iraq.Qayyarah_Airfield_West
-- * AIRBASE.Iraq.Baghdad_International_Airport
-- * AIRBASE.Iraq.Balad_Airbase
-- * AIRBASE.Iraq.Bashur_Airport
-- * AIRBASE.Iraq.Erbil_International_Airport
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
-- * AIRBASE.Iraq.H2_Airbase
-- * AIRBASE.Iraq.H3_Main_Airbase
-- * AIRBASE.Iraq.H3_Northwest_Airbase
-- * AIRBASE.Iraq.H3_Southwest_Airbase
-- * AIRBASE.Iraq.K1_Base
-- * AIRBASE.Iraq.Kirkuk_International_Airport
-- * AIRBASE.Iraq.Mosul_International_Airport
-- * AIRBASE.Iraq.Qayyarah_Airfield_West
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
--
-- @field Iraq
AIRBASE.Iraq = {
["Baghdad_International_Airport"] = "Baghdad International Airport",
["Sulaimaniyah_International_Airport"] = "Sulaimaniyah International Airport",
["Al_Sahra_Airport"] = "Al-Sahra Airport",
["Erbil_International_Airport"] = "Erbil International Airport",
["Al_Taji_Airport"] = "Al-Taji Airport",
["Al_Asad_Airbase"] = "Al-Asad Airbase",
["Al_Kut_Airport"] = "Al-Kut Airport",
["Al_Sahra_Airport"] = "Al-Sahra Airport",
["Al_Salam_Airbase"] = "Al-Salam Airbase",
["Balad_Airbase"] = "Balad Airbase",
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
["Bashur_Airport"] = "Bashur Airport",
["Al_Taji_Airport"] = "Al-Taji Airport",
["Al_Taquddum_Airport"] = "Al-Taquddum Airport",
["Qayyarah_Airfield_West"] = "Qayyarah Airfield West",
["Baghdad_International_Airport"] = "Baghdad International Airport",
["Balad_Airbase"] = "Balad Airbase",
["Bashur_Airport"] = "Bashur Airport",
["Erbil_International_Airport"] = "Erbil International Airport",
["H2_Airbase"] = "H-2 Airbase",
["H3_Main_Airbase"] = "H-3 Main Airbase",
["H3_Northwest_Airbase"] = "H-3 Northwest Airbase",
["H3_Southwest_Airbase"] = "H-3 Southwest Airbase",
["K1_Base"] = "K1 Base",
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
["Mosul_International_Airport"] = "Mosul International Airport",
["Qayyarah_Airfield_West"] = "Qayyarah Airfield West",
["Sulaimaniyah_International_Airport"] = "Sulaimaniyah International Airport",
}
--- Airbases of the Germany Cold War map
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
@@ -1558,6 +1574,17 @@ end
return self
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.
-- @param #AIRBASE self
function AIRBASE:_GetCategory()

View File

@@ -4163,7 +4163,7 @@ function CONTROLLABLE:OptionRestrictBurner( RestrictBurner )
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 #number range Defines the range
-- @return #CONTROLLABLE self
@@ -4188,6 +4188,66 @@ function CONTROLLABLE:OptionAAAttackRange( range )
return nil
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.
-- @param #CONTROLLABLE self
-- @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
DYNAMICCARGO.AircraftTypes = {
["CH-47Fbl1"] = "CH-47Fbl1",
["Mi-8MTV2"] = "CH-47Fbl1",
["Mi-8MT"] = "CH-47Fbl1",
}
--- Helo types possible.
@@ -120,17 +122,30 @@ DYNAMICCARGO.AircraftDimensions = {
["length"] = 11,
["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.
-- @field #string version
DYNAMICCARGO.version="0.0.7"
DYNAMICCARGO.version="0.0.9"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- DONE: Added Mi-8 type and dimensions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor

View File

@@ -231,6 +231,7 @@ GROUP.Attribute = {
GROUND_AAA="Ground_AAA",
GROUND_SAM="Ground_SAM",
GROUND_SHORAD="Ground_SHORAD",
GROUND_BALLISTICMISSILE="Ground_BallisticMissile",
GROUND_OTHER="Ground_OtherGround",
NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
NAVAL_WARSHIP="Naval_WarShip",
@@ -2231,6 +2232,10 @@ function GROUP:Respawn( Template, Reset )
--UTILS.PrintTableToLog(Template)
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(Template.units)
end
-- Spawn new group.
self:ScheduleOnce(0.1,_DATABASE.Spawn,_DATABASE,Template)
--_DATABASE:Spawn(Template)
@@ -2639,6 +2644,8 @@ function GROUP:GetAttribute()
local artillery=self:HasAttribute("Artillery")
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") or self:HasAttribute("Tanks")
local aaa=self:HasAttribute("AAA") and (not self:HasAttribute("SAM elements"))
local ballisticMissile=artillery and self:HasAttribute("SS_missile")
local shorad=self:HasAttribute("SR SAM")
local ewr=self:HasAttribute("EWR")
local ifv=self:HasAttribute("IFV")
local sam=self:HasAttribute("SAM elements") or self:HasAttribute("Optical Tracker")
@@ -2680,6 +2687,8 @@ function GROUP:GetAttribute()
attribute=GROUP.Attribute.GROUND_SAM
elseif aaa then
attribute=GROUP.Attribute.GROUND_AAA
elseif artillery and ballisticMissile then
attribute=GROUP.Attribute.GROUND_BALLISTICMISSILE
elseif artillery then
attribute=GROUP.Attribute.GROUND_ARTILLERY
elseif tank then
@@ -3192,3 +3201,60 @@ function GROUP:IsAAA()
end
return isAAA
end
--- This function uses Disposition and other fallback logic to find better ground positions for ground units.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
--- Uses UTILS.ValidateAndRepositionGroundUnits.
-- @param #GROUP self
-- @param #boolean Enabled Enable/disable the feature.
function GROUP:SetValidateAndRepositionGroundUnits(Enabled)
self.ValidateAndRepositionGroundUnits = Enabled
end
--- Get the bounding box of the group combining UNIT:GetBoundingBox() units.
-- @param #GROUP self
-- @return DCS#Box3 The bounding box of the GROUP.
-- @return #nil The GROUP does not have any alive units.
function GROUP:GetBoundingBox()
local bbox = { min = { x = math.huge, y = math.huge, z = math.huge },
max = { x = -math.huge, y = -math.huge, z = -math.huge }
}
local Units = self:GetUnits() or {}
if #Units == 0 then
return nil
end
for _, unit in pairs(Units) do
if unit and unit:IsAlive() then
local ubox = unit:GetBoundingBox()
if ubox then
if ubox.min.x < bbox.min.x then
bbox.min.x = ubox.min.x
end
if ubox.min.y < bbox.min.y then
bbox.min.y = ubox.min.y
end
if ubox.min.z < bbox.min.z then
bbox.min.z = ubox.min.z
end
if ubox.max.x > bbox.max.x then
bbox.max.x = ubox.max.x
end
if ubox.max.y > bbox.max.y then
bbox.max.y = ubox.max.y
end
if ubox.max.z > bbox.max.z then
bbox.max.z = ubox.max.z
end
end
end
end
return bbox
end

View File

@@ -361,15 +361,17 @@ function POSITIONABLE:GetCoord()
-- Get the current position.
local PositionableVec3 = self:GetVec3()
if self.coordinate then
-- Update COORDINATE from 3D vector.
self.coordinate:UpdateFromVec3( PositionableVec3 )
else
-- New COORDINATE.
self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
end
if PositionableVec3 then
if self.coordinate then
-- Update COORDINATE from 3D vector.
self.coordinate:UpdateFromVec3( PositionableVec3 )
else
-- New COORDINATE.
self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
end
return self.coordinate
return self.coordinate
end
end
-- Error message.
@@ -1863,6 +1865,7 @@ do -- Cargo
["HL_DSHK"] = 6*POSITIONABLE.DefaultInfantryWeight,
["CCKW_353"] = 16*POSITIONABLE.DefaultInfantryWeight, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers,
["MaxxPro_MRAP"] = 7*POSITIONABLE.DefaultInfantryWeight,
["Sd_Kfz_251"] = 10*POSITIONABLE.DefaultInfantryWeight,
}
}

View File

@@ -753,7 +753,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end
end
else
self:E("File for Liquids could not be found: "..tostring(Path).."\\"..tostring(Filename"_Liquids.csv"))
self:E("File for Liquids could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Liquids.csv")
end
end
@@ -773,7 +773,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end
end
else
self:E("File for Aircraft could not be found: "..tostring(Path).."\\"..tostring(Filename"_Aircraft.csv"))
self:E("File for Aircraft could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Aircraft.csv")
end
end
@@ -805,7 +805,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end
end
else
self:E("File for Weapons could not be found: "..tostring(Path).."\\"..tostring(Filename"_Weapons.csv"))
self:E("File for Weapons could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Weapons.csv")
end
end

View File

@@ -377,6 +377,10 @@ function UNIT:ReSpawnAt(Coordinate, Heading)
--self:T( SpawnGroupTemplate )
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(SpawnGroupTemplate.units)
end
_DATABASE:Spawn(SpawnGroupTemplate)
end
@@ -1938,3 +1942,14 @@ end
function UNIT:SetCarrierIlluminationMode(Mode)
UTILS.SetCarrierIlluminationMode(self:GetID(), Mode)
end
--- This function uses Disposition and other fallback logic to find better ground positions for ground units.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
--- Uses UTILS.ValidateAndRepositionGroundUnits.
-- @param #UNIT self
-- @param #boolean Enabled Enable/disable the feature.
function UNIT:SetValidateAndRepositionGroundUnits(Enabled)
self.ValidateAndRepositionGroundUnits = Enabled
end