Compare commits

...

132 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
Thomas
27fe314c1e
Merge pull request #2368 from shaji-Dev/master
[ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits
2025-08-25 12:08:27 +02:00
smiki
2c12cfe4fd [ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits to use for Brigades 2025-08-24 17:06:45 +02:00
smiki
9adf342dd8 [ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits to use for Brigades 2025-08-24 16:45:54 +02:00
smiki
facac82130 [ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits to use for Brigades 2025-08-24 16:44:41 +02:00
Thomas
3e095711f4
Merge pull request #2366 from shaji-Dev/master
[FIXED] Maintain valid unit positions
2025-08-24 16:35:43 +02:00
smiki
ca1ddb4013 Merge remote-tracking branch 'origin/master' 2025-08-24 16:28:08 +02:00
smiki
8169235d2f [FIXED] Maintain valid unit positions 2025-08-24 16:27:55 +02:00
Thomas
4553785918
Merge pull request #2364 from shaji-Dev/master
[ADDED] Validate and Reposition Ground Units algorithm
2025-08-24 15:23:32 +02:00
smiki
78b3efcf00 Validate and Reposition Ground Units algorithm
[ADDED] UTILS.ValidateAndRepositionGroundUnits
[ADDED] SPAWN:InitValidateAndRepositionGroundUnits
[ADDED] CTLD.validateAndRepositionUnits
2025-08-24 15:10:47 +02:00
smiki
67cb844550 Validate and Reposition Ground Units algorithm
[ADDED] UTILS.ValidateAndRepositionGroundUnits
[ADDED] SPAWN:InitValidateAndRepositionGroundUnits
[ADDED] CTLD.validateAndRepositionUnits
2025-08-24 15:07:27 +02:00
Applevangelist
b9cf1e46af xx 2025-08-14 17:17:34 +02:00
Applevangelist
4a04d7cce7 xx 2025-08-14 17:15:43 +02:00
Applevangelist
35f15435a3 #MANTIS - Added Pantsir S1, TOR M2, IRIS-T SLM to main man SAM data (from CH mod) 2025-08-14 09:12:58 +02:00
Applevangelist
4c97d966a2 #MSRS - align google voices catalog with new voice types. 2025-08-10 13:20:22 +02:00
Applevangelist
674c6eec81 More randomness in functions using random coordinates 2025-08-07 11:30:44 +02:00
Thomas
c75c3d8777
Merge pull request #2361 from FlightControl-Master/Applevangelist-score-1
Update Scoring.lua
2025-08-07 11:16:29 +02:00
Thomas
4fa63986dc
Update Scoring.lua
Further changes
2025-08-07 11:16:07 +02:00
Thomas
029f7a3f5c
Update Scoring.lua
Better check for Scenery hits where target category is usually nil
2025-08-06 12:27:28 +02:00
Thomas
e9194c59f4
Merge pull request #2358 from FlightControl-Master/Applevangelist-patch-1
Update Airbase.lua
2025-08-04 16:11:00 +02:00
Thomas
c8d693c8e7
Update Airbase.lua
Sinai add'l bases
2025-08-04 16:10:10 +02:00
Thomas
2341014882
Merge pull request #2356 from leka1986/master
Master
2025-08-02 18:34:25 +02:00
leka1986
eb15fadcfe Added SetPartlyInside. if used, it the :Trigger will trigger as soon as any of the group units enteres the zone even if they are far apart. 2025-08-02 17:40:55 +02:00
Applevangelist
13fa8f373e #MANTIS - added radar entry for Dog Ear and Nike 2025-08-01 14:02:57 +02:00
Applevangelist
b318e8ae13 #AIRBOSS - Added :SetCarrierIllumination(Mode) 2025-07-31 09:47:54 +02:00
Thomas
7e963bef41
Merge pull request #2354 from shaji-Dev/master
[ADDED] `UTILS.ShowPicture` For all, coalition, country, group and unit
2025-07-31 09:26:09 +02:00
smiki
933000ffc7 [ADDED] UNIT:SetCarrierIlluminationMode 2025-07-31 09:06:19 +02:00
smiki
9b217e1c97 [ADDED] UTILS.ShowPicture For all, coalition, country, group and unit
[ADDED] `UTILS.ShowHelperGateForUnit`
2025-07-31 08:57:20 +02:00
smiki
324f4944b4 [ADDED] UTILS.ShowPicture For all, coalition, country, group and unit
[ADDED] `UTILS.ShowHelperGateForUnit`
2025-07-31 08:54:16 +02:00
Applevangelist
f735f1eb53 CTLD - correct ground speed for routing 2025-07-29 17:38:35 +02:00
Thomas
7149226283
Merge pull request #2353 from shaji-Dev/master
[CHANGED] Use file path instead of resource key
2025-07-29 13:04:46 +02:00
shaji-Dev
4164a5288a
Merge branch 'FlightControl-Master:master' into master 2025-07-29 12:50:45 +02:00
smiki
1992276b07 Merge remote-tracking branch 'origin/master' 2025-07-29 12:50:27 +02:00
smiki
21a7023b7b Removed getValueResourceByKey UTILS.ShowPicture and UTILS.SetMissionBriefing to use full file paths 2025-07-29 12:50:19 +02:00
Applevangelist
f094716b73 CTLD - Added option for Vehicle Formation when going to a MOVE zone. 2025-07-29 12:04:41 +02:00
Applevangelist
4b1888a34d CSAR - Allow also the initial down message to be suppressed 2025-07-29 10:02:22 +02:00
Applevangelist
b9be3aa7f8 xx 2025-07-27 14:50:45 +02:00
Thomas
fd2dacaefb
Merge pull request #2351 from shaji-Dev/master
[ADDED] `UTILS.LoadMission` and `UTILS.SetMissionBriefing`
2025-07-27 14:15:16 +02:00
smiki
cc60e85901 [ADDED] UTILS.LoadMission and UTILS.SetMissionBriefing 2025-07-27 13:18:56 +02:00
27 changed files with 4342 additions and 3101 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

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

View File

@ -777,7 +777,9 @@ do -- COORDINATE
-- @return DCS#Vec2 Vec2 -- @return DCS#Vec2 Vec2
function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius ) function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius )
self:F2( { OuterRadius, InnerRadius } ) self:F2( { OuterRadius, InnerRadius } )
math.random()
math.random()
math.random()
local Theta = 2 * math.pi * math.random() local Theta = 2 * math.pi * math.random()
local Radials = math.random() + math.random() local Radials = math.random() + math.random()
if Radials > 1 then if Radials > 1 then
@ -1635,6 +1637,7 @@ do -- COORDINATE
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
RoutePoint.linkUnit = AirbaseID RoutePoint.linkUnit = AirbaseID
RoutePoint.helipadId = AirbaseID RoutePoint.helipadId = AirbaseID
RoutePoint.airdromeId = airbase:IsAirdrome() and AirbaseID or nil
elseif AirbaseCategory == Airbase.Category.AIRDROME then elseif AirbaseCategory == Airbase.Category.AIRDROME then
RoutePoint.airdromeId = AirbaseID RoutePoint.airdromeId = AirbaseID
else else

View File

@ -6691,6 +6691,8 @@ do -- SET_ZONE
-- --
-- -- Stop watching after 1 hour -- -- Stop watching after 1 hour
-- zoneset:__TriggerStop(3600) -- zoneset:__TriggerStop(3600)
-- -- Call :SetPartlyInside() on any zone (or SET_ZONE) if you want GROUPs to count as inside when any of their units enters even if they are far apart.
-- -- Make sure to call :SetPartlyInside() before :Trigger()!.
function SET_ZONE:Trigger(Objects) function SET_ZONE:Trigger(Objects)
--self:I("Added Set_Zone Trigger") --self:I("Added Set_Zone Trigger")
self:AddTransition("*","TriggerStart","TriggerRunning") self:AddTransition("*","TriggerStart","TriggerRunning")
@ -6741,6 +6743,20 @@ do -- SET_ZONE
-- @param Core.Zone#ZONE_BASE Zone The zone left. -- @param Core.Zone#ZONE_BASE Zone The zone left.
end end
--- Toggle “partly-inside” handling for every zone in the set when those zones are used with :Trigger().
-- * Call with no argument or **true** → enable for all.
-- * Call with **false** → disable again (handy if it was enabled before).
-- @param #SET_ZONE self
-- @return #SET_ZONE self
function SET_ZONE:SetPartlyInside(state)
for _,Zone in pairs(self.Set) do
if Zone.SetPartlyInside then
Zone:SetPartlyInside(state)
end
end
return self
end
--- (Internal) Check the assigned objects for being in/out of the zone --- (Internal) Check the assigned objects for being in/out of the zone
-- @param #SET_ZONE self -- @param #SET_ZONE self
-- @param #boolean fromstart If true, do the init of the objects -- @param #boolean fromstart If true, do the init of the objects
@ -6776,8 +6792,13 @@ do -- SET_ZONE
-- has not been tagged previously - wasn't in set! -- has not been tagged previously - wasn't in set!
obj.TriggerInZone[_zone.ZoneName] = false obj.TriggerInZone[_zone.ZoneName] = false
end end
-- is obj in zone? -- is obj in this zone?
local inzone = _zone:IsCoordinateInZone(obj:GetCoordinate()) local inzone
if _zone.PartlyInside and obj.ClassName == "GROUP" then
inzone = obj:IsAnyInZone(_zone) -- TRUE as soon as any unit is inside
else
inzone = _zone:IsCoordinateInZone(obj:GetCoordinate()) -- original centroid test
end
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone)) --self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
if inzone and not obj.TriggerInZone[_zone.ZoneName] then if inzone and not obj.TriggerInZone[_zone.ZoneName] then
-- wasn't in zone before -- wasn't in zone before
@ -7844,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

@ -1049,6 +1049,23 @@ function SPAWN:InitSetUnitAbsolutePositions(Positions)
return self return self
end end
--- 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.
--- 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.
-- @return #SPAWN
function SPAWN:InitValidateAndRepositionGroundUnits(OnOff, MaxRadius, Spacing)
self.SpawnValidateAndRepositionGroundUnits = OnOff
self.SpawnValidateAndRepositionGroundUnitsRadius = MaxRadius
self.SpawnValidateAndRepositionGroundUnitsSpacing = Spacing
return self
end
--- This method is rather complicated to understand. But I'll try to explain. --- This method is rather complicated to understand. But I'll try to explain.
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor, -- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
-- but they will all follow the same Template route and have the same prefix name. -- but they will all follow the same Template route and have the same prefix name.
@ -1830,6 +1847,12 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
SpawnTemplate.hidden=self.SpawnHiddenOnMap SpawnTemplate.hidden=self.SpawnHiddenOnMap
end end
if self.SpawnValidateAndRepositionGroundUnits then
local units = SpawnTemplate.units
local gPos = { x = SpawnTemplate.x, y = SpawnTemplate.y }
UTILS.ValidateAndRepositionGroundUnits(units, gPos, self.SpawnValidateAndRepositionGroundUnitsRadius, self.SpawnValidateAndRepositionGroundUnitsSpacing)
end
-- Set country, coalition and category. -- Set country, coalition and category.
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID

View File

@ -378,6 +378,20 @@ function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
return self return self
end 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. --- 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 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. -- 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. -- Add static to the game.
local Static=nil --DCS#StaticObject 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 if self.InitFarp then
local TemplateGroup={} local TemplateGroup={}
@ -611,16 +633,15 @@ 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
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 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 return mystatic
end end

View File

@ -70,6 +70,7 @@
-- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value. -- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value.
-- @field #number Surface Type of surface. Only determined at the center of the zone! -- @field #number Surface Type of surface. Only determined at the center of the zone!
-- @field #number Checktime Check every Checktime seconds, used for ZONE:Trigger() -- @field #number Checktime Check every Checktime seconds, used for ZONE:Trigger()
-- @field #boolean PartlyInside When called, a GROUP is considered inside as soon as any of its units enters the zone even if they are far apart.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@ -612,6 +613,8 @@ end
-- --
-- -- Stop watching the zone after 1 hour -- -- Stop watching the zone after 1 hour
-- triggerzone:__TriggerStop(3600) -- triggerzone:__TriggerStop(3600)
-- -- Call :SetPartlyInside() if you use SET_GROUP to count as inside when any of their units enters even when they are far apart.
-- -- Make sure to call :SetPartlyInside() before :Trigger()!
function ZONE_BASE:Trigger(Objects) function ZONE_BASE:Trigger(Objects)
--self:I("Added Zone Trigger") --self:I("Added Zone Trigger")
self:SetStartState("TriggerStopped") self:SetStartState("TriggerStopped")
@ -680,6 +683,16 @@ function ZONE_BASE:Trigger(Objects)
end end
--- Toggle “partly-inside” handling for this zone. To be used before :Trigger().
-- * Default:* flag is **false** until you call the method.
-- * Call with no argument or with **true** → enable.
-- * Call with **false** → disable again (handy if it was enabled before).
-- @param #ZONE_BASE self
-- @return #ZONE_BASE self
function ZONE_BASE:SetPartlyInside(state)
self.PartlyInside = state or not ( state == false )
return self
end
--- (Internal) Check the assigned objects for being in/out of the zone --- (Internal) Check the assigned objects for being in/out of the zone
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param #boolean fromstart If true, do the init of the objects -- @param #boolean fromstart If true, do the init of the objects
@ -718,7 +731,12 @@ function ZONE_BASE:_TriggerCheck(fromstart)
obj.TriggerInZone[self.ZoneName] = false obj.TriggerInZone[self.ZoneName] = false
end end
-- is obj in zone? -- is obj in zone?
local inzone = self:IsCoordinateInZone(obj:GetCoordinate()) local inzone
if self.PartlyInside and obj.ClassName == "GROUP" then
inzone = obj:IsAnyInZone(self) -- TRUE if any unit is inside
else
inzone = self:IsCoordinateInZone(obj:GetCoordinate()) -- original barycentre test
end
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone)) --self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
if inzone and obj.TriggerInZone[self.ZoneName] then if inzone and obj.TriggerInZone[self.ZoneName] then
-- just count -- just count
@ -1162,15 +1180,13 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
local function EvaluateZone( ZoneObject ) 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: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. -- Get object category.
local ObjectCategory = Object.getCategory(ZoneObject) 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 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 local Include = false
if not UnitCategories then if not UnitCategories then
-- Anything found is included. -- Anything found is included.
@ -1554,6 +1570,10 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
local _inner = inner or 0 local _inner = inner or 0
local _outer = outer or self:GetRadius() local _outer = outer or self:GetRadius()
math.random()
math.random()
math.random()
if surfacetypes and type(surfacetypes)~="table" then if surfacetypes and type(surfacetypes)~="table" then
surfacetypes={surfacetypes} surfacetypes={surfacetypes}
end end
@ -1914,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
@ -2051,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.
@ -2918,6 +2969,11 @@ end
function ZONE_POLYGON_BASE:GetRandomVec2() function ZONE_POLYGON_BASE:GetRandomVec2()
-- make sure we assign weights to the triangles based on their surface area, otherwise -- make sure we assign weights to the triangles based on their surface area, otherwise
-- we'll be more likely to generate random points in smaller triangles -- we'll be more likely to generate random points in smaller triangles
math.random()
math.random()
math.random()
local weights = {} local weights = {}
for _, triangle in pairs(self._Triangles) do for _, triangle in pairs(self._Triangles) do
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
@ -3281,14 +3337,12 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
local function EvaluateZone( ZoneObject ) local function EvaluateZone( ZoneObject )
if ZoneObject then if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
local ObjectCategory = Object.getCategory(ZoneObject) 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 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 local Include = false
if not UnitCategories then if not UnitCategories then
-- Anything found is included. -- Anything found is included.
@ -3320,7 +3374,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
end end
-- trying with box search -- 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 SceneryType = ZoneObject:getTypeName()
local SceneryName = ZoneObject:getName() local SceneryName = ZoneObject:getName()
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {} self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}

View File

@ -22,7 +22,7 @@
-- @module Functional.Mantis -- @module Functional.Mantis
-- @image Functional.Mantis.jpg -- @image Functional.Mantis.jpg
-- --
-- Last Update: July 2025 -- Last Update: August 2025
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **MANTIS** class, extends Core.Base#BASE --- **MANTIS** class, extends Core.Base#BASE
@ -108,10 +108,15 @@
-- * Patriot -- * Patriot
-- * Rapier -- * Rapier
-- * Roland -- * Roland
-- * IRIS-T SLM
-- * Pantsir S1
-- * TOR M2
-- * C-RAM
-- * Silkworm (though strictly speaking this is a surface to ship missile) -- * Silkworm (though strictly speaking this is a surface to ship missile)
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19 -- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!) -- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!)
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2, SAMP/T Block 1, SAMP/T Block 1INT, SAMP/T Block2 -- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2, SAMP/T Block 1, SAMP/T Block 1INT, SAMP/T Block2
-- * Other Mods: Nike
-- --
-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M -- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M
-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA" -- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA"
@ -275,7 +280,7 @@
MANTIS = { MANTIS = {
ClassName = "MANTIS", ClassName = "MANTIS",
name = "mymantis", name = "mymantis",
version = "0.9.32", version = "0.9.34",
SAM_Templates_Prefix = "", SAM_Templates_Prefix = "",
SAM_Group = nil, SAM_Group = nil,
EWR_Templates_Prefix = "", EWR_Templates_Prefix = "",
@ -384,7 +389,7 @@ MANTIS.SamData = {
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" }, ["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" }, ["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" },
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" }, ["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
["HEMTT_C-RAM_Phalanx"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" }, ["C-RAM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" },
-- units from HDS Mod, multi launcher options is tricky -- units from HDS Mod, multi launcher options is tricky
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"}, ["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
["SA-17"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17" }, ["SA-17"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17" },
@ -393,6 +398,12 @@ MANTIS.SamData = {
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" }, ["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" }, ["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" }, ["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
["NIKE"] = { Range=155, Blindspot=6, Height=30, Type="Long", Radar="HIPAR" },
["Dog Ear"] = { Range=11, Blindspot=0, Height=9, Type="Point", Radar="Dog Ear", Point="true" },
-- CH Added to DCS core 2.9.19.x
["Pantsir S1"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1" , Point="true" },
["Tor M2"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
["IRIS-T SLM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
} }
--- SAM data HDS --- SAM data HDS
@ -458,15 +469,15 @@ MANTIS.SamDataCH = {
-- https://www.currenthill.com/ -- https://www.currenthill.com/
-- group name MUST contain CHM to ID launcher type correctly! -- group name MUST contain CHM to ID launcher type correctly!
["2S38 CHM"] = { Range=6, Blindspot=0.1, Height=4.5, Type="Short", Radar="2S38" }, ["2S38 CHM"] = { Range=6, Blindspot=0.1, Height=4.5, Type="Short", Radar="2S38" },
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" }, ["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1", Point="true" },
["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" }, ["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
["PGL-625 CHM"] = { Range=10, Blindspot=1, Height=5, Type="Short", Radar="PGL_625" }, ["PGL-625 CHM"] = { Range=10, Blindspot=1, Height=5, Type="Short", Radar="PGL_625" },
["HQ-17A CHM"] = { Range=15, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" }, ["HQ-17A CHM"] = { Range=15, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
["M903PAC2 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" }, ["M903PAC2 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
["M903PAC3 CHM"] = { Range=160, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" }, ["M903PAC3 CHM"] = { Range=160, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" }, ["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" }, ["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2K", Point="true" },
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" }, ["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Point", Radar="TorM2M", Point="true" },
["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" }, ["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" }, ["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" }, ["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" },
@ -882,7 +893,11 @@ do
self.AcceptZones = AcceptZones or {} self.AcceptZones = AcceptZones or {}
self.RejectZones = RejectZones or {} self.RejectZones = RejectZones or {}
self.ConflictZones = ConflictZones or {} self.ConflictZones = ConflictZones or {}
if #self.AcceptZones > 0 or #self.RejectZones > 0 or #self.ConflictZones > 0 then self.AcceptZonesNo = UTILS.TableLength(self.AcceptZones)
self.RejectZonesNo = UTILS.TableLength(self.RejectZones)
self.ConflictZonesNo = UTILS.TableLength(self.ConflictZones)
self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
if self.AcceptZonesNo > 0 or self.RejectZonesNo > 0 or self.ConflictZonesNo > 0 then
self.usezones = true self.usezones = true
end end
return self return self
@ -1274,7 +1289,8 @@ do
self:T(self.lid.."_CheckCoordinateInZones") self:T(self.lid.."_CheckCoordinateInZones")
local inzone = false local inzone = false
-- acceptzones -- acceptzones
if #self.AcceptZones > 0 then self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
if self.AcceptZonesNo > 0 then
for _,_zone in pairs(self.AcceptZones) do for _,_zone in pairs(self.AcceptZones) do
local zone = _zone -- Core.Zone#ZONE local zone = _zone -- Core.Zone#ZONE
if zone:IsCoordinateInZone(coord) then if zone:IsCoordinateInZone(coord) then
@ -1285,7 +1301,7 @@ do
end end
end end
-- rejectzones -- rejectzones
if #self.RejectZones > 0 and inzone then -- maybe in accept zone, but check the overlaps if self.RejectZonesNo > 0 then
for _,_zone in pairs(self.RejectZones) do for _,_zone in pairs(self.RejectZones) do
local zone = _zone -- Core.Zone#ZONE local zone = _zone -- Core.Zone#ZONE
if zone:IsCoordinateInZone(coord) then if zone:IsCoordinateInZone(coord) then
@ -1296,7 +1312,7 @@ do
end end
end end
-- conflictzones -- conflictzones
if #self.ConflictZones > 0 and not inzone then -- if not already accepted, might be in conflict zones if self.ConflictZonesNo > 0 then
for _,_zone in pairs(self.ConflictZones) do for _,_zone in pairs(self.ConflictZones) do
local zone = _zone -- Core.Zone#ZONE local zone = _zone -- Core.Zone#ZONE
if zone:IsCoordinateInZone(coord) then if zone:IsCoordinateInZone(coord) then
@ -1362,6 +1378,7 @@ do
end end
-- check accept/reject zones -- check accept/reject zones
local zonecheck = true local zonecheck = true
self:T("self.usezones = "..tostring(self.usezones))
if self.usezones then if self.usezones then
-- DONE -- DONE
zonecheck = self:_CheckCoordinateInZones(coord) zonecheck = self:_CheckCoordinateInZones(coord)

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. -- Create the CSV file.
self.AutoSavePath = SavePath self.AutoSavePath = SavePath
self.AutoSave = AutoSave or true self.AutoSave = (AutoSave == nil or AutoSave == true) and true or false
self:OpenCSV( GameName ) if self.AutoSavePath and self.AutoSave == true then
self:OpenCSV( GameName )
end
return self return self
@ -985,6 +987,7 @@ function SCORING:_EventOnHit( Event )
local TargetUnitCoalition = nil local TargetUnitCoalition = nil
local TargetUnitCategory = nil local TargetUnitCategory = nil
local TargetUnitType = nil local TargetUnitType = nil
local TargetIsScenery = false
if Event.IniDCSUnit then if Event.IniDCSUnit then
@ -1025,6 +1028,12 @@ function SCORING:_EventOnHit( Event )
TargetCategory = Event.TgtCategory TargetCategory = Event.TgtCategory
TargetType = Event.TgtTypeName TargetType = Event.TgtTypeName
-- Scenery hit
if (not TargetCategory) and TargetUNIT ~= nil and TargetUnit:IsInstanceOf("SCENERY") then
TargetCategory = Unit.Category.STRUCTURE
TargetIsScenery = true
end
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
TargetUnitCategory = _SCORINGCategory[TargetCategory] TargetUnitCategory = _SCORINGCategory[TargetCategory]
TargetUnitType = TargetType TargetUnitType = TargetType
@ -1117,17 +1126,22 @@ function SCORING:_EventOnHit( Event )
MESSAGE.Type.Update ) MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else elseif TargetIsScenery ~= true then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update ) MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
elseif TargetIsScenery == true then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object." .. " Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end end
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end end
else -- A scenery object was hit. else -- A scenery object was hit.
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.", MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit nothing special.",
MESSAGE.Type.Update ) MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@ -1923,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

@ -4247,6 +4247,16 @@ function WAREHOUSE:_AssetItemInfo(asset)
self:T3({Template=asset.template}) self:T3({Template=asset.template})
end 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 #boolean Enabled Enable/disable the feature.
function WAREHOUSE:SetValidateAndRepositionGroundUnits(Enabled)
self.ValidateAndRepositionGroundUnits = Enabled
end
--- On after "NewAsset" event. A new asset has been added to the warehouse stock. --- On after "NewAsset" event. A new asset has been added to the warehouse stock.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param #string From From state. -- @param #string From From state.
@ -5965,6 +5975,10 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, late
template.y = coord.z template.y = coord.z
template.alt = coord.y template.alt = coord.y
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(template.units)
end
-- Spawn group. -- Spawn group.
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP

View File

@ -39,6 +39,8 @@
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_\(LHA-6\)) (LHA-6) -- * [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) -- * [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) -- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_\(L02\)) (L02)
-- * BONHOMMERICHARD [VWV Mod]
-- * ENTERPRISE66 [VWV Mod]
-- --
-- **Supported Aircraft:** -- **Supported Aircraft:**
-- --
@ -1317,6 +1319,10 @@ AIRBOSS.AircraftCarrier={
-- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module] -- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module]
-- @field #string VINSON USS Carl Vinson (CVN-70) [Deprecated!] -- @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 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 HERMES HMS Hermes (R12) [V/STOL Carrier]
-- @field #string INVINCIBLE HMS Invincible (R05) [V/STOL Carrier] -- @field #string INVINCIBLE HMS Invincible (R05) [V/STOL Carrier]
-- @field #string TARAWA USS Tarawa (LHA-1) [V/STOL Carrier] -- @field #string TARAWA USS Tarawa (LHA-1) [V/STOL Carrier]
@ -1331,8 +1337,12 @@ AIRBOSS.CarrierType = {
TRUMAN = "CVN_75", TRUMAN = "CVN_75",
STENNIS = "Stennis", STENNIS = "Stennis",
FORRESTAL = "Forrestal", FORRESTAL = "Forrestal",
ENTERPRISE66 = "USS Enterprise 1966",
ENTERPRISEMODERN = "cvn-65",
VINSON = "VINSON", VINSON = "VINSON",
ESSEX = "Essex", ESSEX = "Essex",
BONHOMMERICHARD = "USS Bon Homme Richard",
ESSEXSCB125 = "essex_scb125",
HERMES = "HERMES81", HERMES = "HERMES81",
INVINCIBLE = "hms_invincible", INVINCIBLE = "hms_invincible",
TARAWA = "LHA_Tarawa", TARAWA = "LHA_Tarawa",
@ -1756,7 +1766,7 @@ AIRBOSS.MenuF10Root = nil
--- Airboss class version. --- Airboss class version.
-- @field #string version -- @field #string version
AIRBOSS.version = "1.4.1" AIRBOSS.version = "1.4.2"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -2019,11 +2029,19 @@ function AIRBOSS:New( carriername, alias )
self:_InitNimitz() self:_InitNimitz()
elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then
self:_InitForrestal() 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 elseif self.carriertype == AIRBOSS.CarrierType.VINSON then
-- Carl Vinson is legacy now. -- Carl Vinson is legacy now.
self:_InitStennis() self:_InitStennis()
elseif self.carriertype == AIRBOSS.CarrierType.ESSEX then elseif self.carriertype == AIRBOSS.CarrierType.ESSEX then
self:_InitEssex() 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 elseif self.carriertype == AIRBOSS.CarrierType.HERMES then
-- Hermes parameters. -- Hermes parameters.
self:_InitHermes() self:_InitHermes()
@ -2412,6 +2430,16 @@ end
-- USER API Functions -- USER API Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set the carrier illumination mode.
-- @param #AIRBOSS self
-- @param #number Mode Options are: -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
-- @return #AIRBOSS self
function AIRBOSS:SetCarrierIllumination(Mode)
self.carrier:SetCarrierIlluminationMode(Mode)
return self
end
--- Set welcome messages for players. --- Set welcome messages for players.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #boolean Switch If true, display welcome message to player. -- @param #boolean Switch If true, display welcome message to player.
@ -3100,8 +3128,8 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
self.SRS:SetCulture(Culture or "en-US") self.SRS:SetCulture(Culture or "en-US")
--self.SRS:SetFrequencies(Frequencies) --self.SRS:SetFrequencies(Frequencies)
self.SRS:SetGender(Gender or "male") self.SRS:SetGender(Gender or "male")
self.SRS:SetPath(PathToSRS) --self.SRS:SetPath(PathToSRS)
self.SRS:SetPort(Port or 5002) self.SRS:SetPort(Port or MSRS.port or 5002)
self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS") self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS")
self.SRS:SetCoordinate(self.carrier:GetCoordinate()) self.SRS:SetCoordinate(self.carrier:GetCoordinate())
self.SRS:SetVolume(Volume or 1) self.SRS:SetVolume(Volume or 1)
@ -3112,7 +3140,10 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
if Voice then if Voice then
self.SRS:SetVoice(Voice) self.SRS:SetVoice(Voice)
end 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 -- SRSQUEUE
self.SRSQ = MSRSQUEUE:New("AIRBOSS") self.SRSQ = MSRSQUEUE:New("AIRBOSS")
self.SRSQ:SetTransmitOnlyWithPlayers(true) self.SRSQ:SetTransmitOnlyWithPlayers(true)
@ -4643,6 +4674,26 @@ function AIRBOSS:_InitForrestal()
end 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. --- Init parameters for Essec class carriers.
-- @param #AIRBOSS self -- @param #AIRBOSS self
function AIRBOSS:_InitEssex() function AIRBOSS:_InitEssex()
@ -4688,6 +4739,35 @@ function AIRBOSS:_InitEssex()
end 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. --- Init parameters for R12 HMS Hermes carrier.
-- @param #AIRBOSS self -- @param #AIRBOSS self
function AIRBOSS:_InitHermes() function AIRBOSS:_InitHermes()
@ -6870,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 )
@ -7523,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
@ -8062,8 +8145,7 @@ 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
@ -9564,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 )
@ -9575,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
@ -10281,19 +10268,19 @@ function AIRBOSS:_Groove( playerData )
if rho >= RAR and rho <= RIM then if rho >= RAR and rho <= RIM then
if gd.LUE > 0.22 and lineupError < -0.22 then if gd.LUE > 0.22 and lineupError < -0.22 then
env.info " Drift Right across centre ==> DR-" 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 ) ) 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 elseif gd.LUE < -0.22 and lineupError > 0.22 then
env.info " Drift Left ==> DL-" 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 ) ) 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 elseif gd.LUE > 0.13 and lineupError < -0.14 then
env.info " Little Drift Right across centre ==> (DR-)" 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 ) ) 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 elseif gd.LUE < -0.13 and lineupError > 0.14 then
env.info " Little Drift Left across centre ==> (DL-)" 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 ) ) 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
end end
@ -11971,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
@ -12240,8 +12229,8 @@ function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord )
local magvar= magnetic and self.magvar or 0 local magvar= magnetic and self.magvar or 0
-- 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
local intowind = self:GetHeadingIntoWind_old(vdeck) -- VNAO Edit: Using old heading into wind algorithm
return intowind, v return intowind, v
end end
@ -12671,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 )
@ -12694,6 +12683,7 @@ function AIRBOSS:_LSOgrade( playerData )
local nS=count(G, '%(') local nS=count(G, '%(')
local nN=N-nS-nL 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. -- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn.
local Tgroove=playerData.Tgroove local Tgroove=playerData.Tgroove
@ -12723,7 +12713,6 @@ function AIRBOSS:_LSOgrade( playerData )
else else
if vtol then 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 -- 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. -- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation.
@ -12865,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)
@ -12950,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
@ -12970,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

File diff suppressed because it is too large Load Diff

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)
@ -867,6 +869,8 @@ do
-- my_ctld.TroopUnloadDistHoverHook = 5 -- When hovering, unload troops this far behind the Chinook -- my_ctld.TroopUnloadDistHoverHook = 5 -- When hovering, unload troops this far behind the Chinook
-- my_ctld.showstockinmenuitems = false -- When set to true, the menu lines will also show the remaining items in stock (that is, if you set any), downside is that the menu for all will be build every 30 seconds anew. -- my_ctld.showstockinmenuitems = false -- When set to true, the menu lines will also show the remaining items in stock (that is, if you set any), downside is that the menu for all will be build every 30 seconds anew.
-- my_ctld.onestepmenu = false -- When set to true, the menu will create Drop and build, Get and load, Pack and remove, Pack and load, Pack. it will be a 1 step solution. -- my_ctld.onestepmenu = false -- When set to true, the menu will create Drop and build, Get and load, Pack and remove, Pack and load, Pack. it will be a 1 step solution.
-- my_ctld.VehicleMoveFormation = AI.Task.VehicleFormation.VEE -- When a group moves to a MOVE zone, then it takes this formation. Can be a table of formations, which are then randomly chosen. Defaults to "Vee".
-- my_ctld.validateAndRepositionUnits = false -- Uses Disposition and other logic to find better ground positions for ground units avoiding trees, water, roads, runways, map scenery, statics and other units in the area. (Default is false)
-- --
-- ## 2.1 CH-47 Chinook support -- ## 2.1 CH-47 Chinook support
-- --
@ -1294,6 +1298,7 @@ CTLD = {
LoadedGroupsTable = {}, LoadedGroupsTable = {},
keeploadtable = true, keeploadtable = true,
allowCATransport = false, allowCATransport = false,
VehicleMoveFormation = AI.Task.VehicleFormation.VEE,
} }
------------------------------ ------------------------------
@ -1393,6 +1398,7 @@ CTLD.UnitTypeCapabilities = {
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers. ["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. --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"] = {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 ["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 ["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 ["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
@ -1414,7 +1420,7 @@ CTLD.FixedWingTypes = {
--- CTLD class version. --- CTLD class version.
-- @field #string version -- @field #string version
CTLD.version="1.3.36" CTLD.version="1.3.38"
--- Instantiate a new CTLD. --- Instantiate a new CTLD.
-- @param #CTLD self -- @param #CTLD self
@ -1552,8 +1558,11 @@ 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
-- added support Hercules Mod -- added support Hercules Mod
self.enableHercules = false -- deprecated self.enableHercules = false -- deprecated
self.enableFixedWing = false self.enableFixedWing = false
@ -1561,6 +1570,8 @@ function CTLD:New(Coalition, Prefixes, Alias)
self.FixedMaxAngels = 2000 -- for troop/cargo drop via chute self.FixedMaxAngels = 2000 -- for troop/cargo drop via chute
self.FixedMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps self.FixedMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps
self.validateAndRepositionUnits = false -- 280 kph or 150kn eq 77 mps
-- message suppression -- message suppression
self.suppressmessages = false self.suppressmessages = false
@ -3321,6 +3332,7 @@ function CTLD:_LoadCratesNearby(Group, Unit)
self:_RefreshLoadCratesMenu(Group, Unit) self:_RefreshLoadCratesMenu(Group, Unit)
-- clean up real world crates -- clean up real world crates
self:_CleanupTrackedCrates(crateidsloaded) self:_CleanupTrackedCrates(crateidsloaded)
self:__CratesPickedUp(1, Group, Unit, loaded.Cargo)
end end
end end
return self return self
@ -3667,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
@ -3731,6 +3743,7 @@ function CTLD:_UnloadTroops(Group, Unit)
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitDelayOff() :InitDelayOff()
:InitSetUnitAbsolutePositions(Positions) :InitSetUnitAbsolutePositions(Positions)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord:GetVec2()) :SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type) self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type)
@ -4177,11 +4190,13 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,Mult
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
--:InitRandomizeUnits(true,20,2) --:InitRandomizeUnits(true,20,2)
:InitDelayOff() :InitDelayOff()
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord) :SpawnFromVec2(randomcoord)
else -- don't random position of e.g. SAM units build as FOB else -- don't random position of e.g. SAM units build as FOB
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitDelayOff() :InitDelayOff()
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord) :SpawnFromVec2(randomcoord)
end end
@ -4197,6 +4212,17 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,Mult
return self return self
end end
--- (Internal) Function to get a vehicle formation for a moving group
-- @param #CTLD self
-- @return #string Formation
function CTLD:_GetVehicleFormation()
local VehicleMoveFormation = self.VehicleMoveFormation or AI.Task.VehicleFormation.VEE
if type(self.VehicleMoveFormation)=="table" then
VehicleMoveFormation = self.VehicleMoveFormation[math.random(1,#self.VehicleMoveFormation)]
end
return VehicleMoveFormation
end
--- (Internal) Function to move group to WP zone. --- (Internal) Function to move group to WP zone.
-- @param #CTLD self -- @param #CTLD self
-- @param Wrapper.Group#GROUP Group The Group to move. -- @param Wrapper.Group#GROUP Group The Group to move.
@ -4211,18 +4237,20 @@ function CTLD:_MoveGroupToZone(Group)
-- yes, we can ;) -- yes, we can ;)
local groupname = Group:GetName() local groupname = Group:GetName()
local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE
local coordinate = zonecoord:GetVec2() local formation = self:_GetVehicleFormation()
--local coordinate = zonecoord:GetVec2()
Group:SetAIOn() Group:SetAIOn()
Group:OptionAlarmStateAuto() Group:OptionAlarmStateAuto()
Group:OptionDisperseOnAttack(30) Group:OptionDisperseOnAttack(30)
Group:OptionROEOpenFirePossible() Group:OptionROEOpenFire()
Group:RouteToVec2(coordinate,5) Group:RouteGroundTo(zonecoord,25,formation)
end end
return self return self
end end
--- (Internal) Housekeeping - Cleanup crates when build --- (Internal) Housekeeping - Cleanup crates when build
-- @param #CTLD self -- @param #CTLD self
--
-- @param #table Crates Table of #CTLD_CARGO objects near the unit. -- @param #table Crates Table of #CTLD_CARGO objects near the unit.
-- @param #CTLD.Buildable Build Table build object. -- @param #CTLD.Buildable Build Table build object.
-- @param #number Number Number of objects in Crates (found) to limit search. -- @param #number Number Number of objects in Crates (found) to limit search.
@ -4488,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
@ -4499,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
@ -4511,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
@ -4522,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
@ -4534,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
@ -4550,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
@ -4560,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
@ -4569,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
@ -5109,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
@ -5194,6 +5230,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias)
:InitDelayOff() :InitDelayOff()
:InitSetUnitAbsolutePositions(Positions) :InitSetUnitAbsolutePositions(Positions)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord:GetVec2()) :SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType) self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType)
@ -5400,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".
@ -5645,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"
@ -5672,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
self:AddZone(ctldzone) if not exists then
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.
@ -6906,6 +7040,7 @@ end
local alias = string.format("%s-%d", _template, math.random(1,100000)) local alias = string.format("%s-%d", _template, math.random(1,100000))
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(randompositions,20,2) :InitRandomizeUnits(randompositions,20,2)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:InitDelayOff() :InitDelayOff()
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp) :OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord) :SpawnFromVec2(randomcoord)
@ -7059,12 +7194,14 @@ end
if canmove then if canmove then
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(true,20,2) :InitRandomizeUnits(true,20,2)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:InitDelayOff() :InitDelayOff()
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp) :OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord) :SpawnFromVec2(randomcoord)
else -- don't random position of e.g. SAM units build as FOB else -- don't random position of e.g. SAM units build as FOB
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitDelayOff() :InitDelayOff()
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp) :OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord) :SpawnFromVec2(randomcoord)
end end
@ -7134,6 +7271,16 @@ end
local filepath = self.filepath local filepath = self.filepath
self:__Save(interval,filepath,filename) self:__Save(interval,filepath,filename)
end end
if type(self.VehicleMoveFormation) == "table" then
local Formations = {}
for _,_formation in pairs(self.VehicleMoveFormation) do
table.insert(Formations,_formation)
end
self.VehicleMoveFormation = nil
self.VehicleMoveFormation = Formations
end
return self return self
end end
@ -7404,8 +7551,11 @@ 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
self:_MoveGroupToZone(Vehicle) 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 end
return self return self
end end

View File

@ -72,7 +72,7 @@ end
--- Checks if a point is contained within the circle. --- Checks if a point is contained within the circle.
-- @param #table point The point to check -- @param #table point The point to check
-- @return #bool True if the point is contained, false otherwise -- @return #boolean True if the point is contained, false otherwise
function CIRCLE:ContainsPoint(point) function CIRCLE:ContainsPoint(point)
if ((point.x - self.CenterVec2.x) ^ 2 + (point.y - self.CenterVec2.y) ^ 2) ^ 0.5 <= self.Radius then if ((point.x - self.CenterVec2.x) ^ 2 + (point.y - self.CenterVec2.y) ^ 2) ^ 0.5 <= self.Radius then
return true return true
@ -226,6 +226,11 @@ end
--- Returns a random Vec2 within the circle. --- Returns a random Vec2 within the circle.
-- @return #table The random Vec2 -- @return #table The random Vec2
function CIRCLE:GetRandomVec2() function CIRCLE:GetRandomVec2()
math.random()
math.random()
math.random()
local angle = math.random() * 2 * math.pi local angle = math.random() * 2 * math.pi
local rx = math.random(0, self.Radius) * math.cos(angle) + self.CenterVec2.x local rx = math.random(0, self.Radius) * math.cos(angle) + self.CenterVec2.x
@ -237,6 +242,11 @@ end
--- Returns a random Vec2 on the border of the circle. --- Returns a random Vec2 on the border of the circle.
-- @return #table The random Vec2 -- @return #table The random Vec2
function CIRCLE:GetRandomVec2OnBorder() function CIRCLE:GetRandomVec2OnBorder()
math.random()
math.random()
math.random()
local angle = math.random() * 2 * math.pi local angle = math.random() * 2 * math.pi
local rx = self.Radius * math.cos(angle) + self.CenterVec2.x local rx = self.Radius * math.cos(angle) + self.CenterVec2.x

View File

@ -352,6 +352,7 @@ end
--- Returns a random Vec2 within the polygon. The Vec2 is weighted by the areas of the triangles that make up the polygon. --- Returns a random Vec2 within the polygon. The Vec2 is weighted by the areas of the triangles that make up the polygon.
-- @return #table The random Vec2 -- @return #table The random Vec2
function POLYGON:GetRandomVec2() function POLYGON:GetRandomVec2()
local weights = {} local weights = {}
for _, triangle in pairs(self.Triangles) do for _, triangle in pairs(self.Triangles) do
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea weights[triangle] = triangle.SurfaceArea / self.SurfaceArea

View File

@ -73,6 +73,11 @@ end
-- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it -- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
-- @return #table The random Vec2 -- @return #table The random Vec2
function TRIANGLE:GetRandomVec2(points) function TRIANGLE:GetRandomVec2(points)
math.random()
math.random()
math.random()
points = points or self.Points points = points or self.Points
local pt = {math.random(), math.random()} local pt = {math.random(), math.random()}
table.sort(pt) table.sort(pt)

View File

@ -443,28 +443,32 @@ MSRS.Voices = {
["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE ["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE
["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE ["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE
["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE ["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE
["en_IN_Standard_A"] = 'en-IN-Standard-A', -- [5] FEMALE -- IN
["en_IN_Standard_B"] = 'en-IN-Standard-B', -- [6] MALE ["en_IN_Standard_A"] = 'en-IN-Standard-A', -- Female
["en_IN_Standard_C"] = 'en-IN-Standard-C', -- [7] MALE ["en_IN_Standard_B"] = 'en-IN-Standard-B', -- Male
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- [8] FEMALE ["en_IN_Standard_C"] = 'en-IN-Standard-C', -- Male
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- Female
["en_IN_Standard_E"] = 'en-IN-Standard-E', -- Female
["en_IN_Standard_F"] = 'en-IN-Standard-F', -- Male
-- 2025 changes -- 2025 changes
["en_GB_Standard_A"] = 'en-GB-Standard-N', -- [9] FEMALE ["en_GB_Standard_A"] = 'en-GB-Standard-A', -- Female
["en_GB_Standard_B"] = 'en-GB-Standard-O', -- [10] MALE ["en_GB_Standard_B"] = 'en-GB-Standard-B', -- Male
["en_GB_Standard_C"] = 'en-GB-Standard-N', -- [11] FEMALE ["en_GB_Standard_C"] = 'en-GB-Standard-C', -- Female
["en_GB_Standard_D"] = 'en-GB-Standard-O', -- [12] MALE ["en_GB_Standard_D"] = 'en-GB-Standard-D', -- Male
["en_GB_Standard_F"] = 'en-GB-Standard-N', -- [13] FEMALE ["en_GB_Standard_F"] = 'en-GB-Standard-F', -- Female
["en_GB_Standard_O"] = 'en-GB-Standard-O', -- [12] MALE ["en_GB_Standard_N"] = 'en-GB-Standard-N', -- Female
["en_GB_Standard_N"] = 'en-GB-Standard-N', -- [13] FEMALE ["en_GB_Standard_O"] = 'en-GB-Standard-O', -- Male
["en_US_Standard_A"] = 'en-US-Standard-A', -- [14] MALE -- US
["en_US_Standard_B"] = 'en-US-Standard-B', -- [15] MALE ["en_US_Standard_A"] = 'en-US-Standard-A', -- Male
["en_US_Standard_C"] = 'en-US-Standard-C', -- [16] FEMALE ["en_US_Standard_B"] = 'en-US-Standard-B', -- Male
["en_US_Standard_D"] = 'en-US-Standard-D', -- [17] MALE ["en_US_Standard_C"] = 'en-US-Standard-C', -- Female
["en_US_Standard_E"] = 'en-US-Standard-E', -- [18] FEMALE ["en_US_Standard_D"] = 'en-US-Standard-D', -- Male
["en_US_Standard_F"] = 'en-US-Standard-F', -- [19] FEMALE ["en_US_Standard_E"] = 'en-US-Standard-E', -- Female
["en_US_Standard_G"] = 'en-US-Standard-G', -- [20] FEMALE ["en_US_Standard_F"] = 'en-US-Standard-F', -- Female
["en_US_Standard_H"] = 'en-US-Standard-H', -- [21] FEMALE ["en_US_Standard_G"] = 'en-US-Standard-G', -- Female
["en_US_Standard_I"] = 'en-US-Standard-I', -- [22] MALE ["en_US_Standard_H"] = 'en-US-Standard-H', -- Female
["en_US_Standard_J"] = 'en-US-Standard-J', -- [23] MALE ["en_US_Standard_I"] = 'en-US-Standard-I', -- Male
["en_US_Standard_J"] = 'en-US-Standard-J', -- Male
-- 2025 catalog changes -- 2025 catalog changes
["fr_FR_Standard_A"] = "fr-FR-Standard-F", -- Female ["fr_FR_Standard_A"] = "fr-FR-Standard-F", -- Female
["fr_FR_Standard_B"] = "fr-FR-Standard-G", -- Male ["fr_FR_Standard_B"] = "fr-FR-Standard-G", -- Male
@ -474,14 +478,15 @@ MSRS.Voices = {
["fr_FR_Standard_G"] = "fr-FR-Standard-G", -- Male ["fr_FR_Standard_G"] = "fr-FR-Standard-G", -- Male
["fr_FR_Standard_F"] = "fr-FR-Standard-F", -- Female ["fr_FR_Standard_F"] = "fr-FR-Standard-F", -- Female
-- 2025 catalog changes -- 2025 catalog changes
["de_DE_Standard_A"] = "de-DE-Standard-G", -- Female ["de_DE_Standard_A"] = 'de-DE-Standard-A', -- Female
["de_DE_Standard_B"] = "de-DE-Standard-H", -- Male ["de_DE_Standard_B"] = 'de-DE-Standard-B', -- Male
["de_DE_Standard_C"] = "de-DE-Standard-G", -- Female ["de_DE_Standard_C"] = 'de-DE-Standard-C', -- Female
["de_DE_Standard_D"] = "de-DE-Standard-H", -- Male ["de_DE_Standard_D"] = 'de-DE-Standard-D', -- Male
["de_DE_Standard_E"] = "de-DE-Standard-H", -- Male ["de_DE_Standard_E"] = 'de-DE-Standard-E', -- Male
["de_DE_Standard_F"] = "de-DE-Standard-G", -- Female ["de_DE_Standard_F"] = 'de-DE-Standard-F', -- Female
["de_DE_Standard_H"] = "de-DE-Standard-H", -- Male ["de_DE_Standard_G"] = 'de-DE-Standard-G', -- Female
["de_DE_Standard_G"] = "de-DE-Standard-G", -- Female ["de_DE_Standard_H"] = 'de-DE-Standard-H', -- Male
-- ES
["es_ES_Standard_A"] = "es-ES-Standard-E", -- Female ["es_ES_Standard_A"] = "es-ES-Standard-E", -- Female
["es_ES_Standard_B"] = "es-ES-Standard-F", -- Male ["es_ES_Standard_B"] = "es-ES-Standard-F", -- Male
["es_ES_Standard_C"] = "es-ES-Standard-E", -- Female ["es_ES_Standard_C"] = "es-ES-Standard-E", -- Female
@ -497,32 +502,36 @@ MSRS.Voices = {
["it_IT_Standard_F"] = "it-IT-Standard-F", -- Male ["it_IT_Standard_F"] = "it-IT-Standard-F", -- Male
}, },
Wavenet = { Wavenet = {
["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- [1] FEMALE ["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- Female
["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- [2] MALE ["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- Male
["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- [3] FEMALE ["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- Female
["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- [4] MALE ["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- Male
["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- [5] FEMALE -- IN
["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- [6] MALE ["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- Female
["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- [7] MALE ["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- Male
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- [8] FEMALE ["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- Male
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- Female
["en_IN_Wavenet_E"] = 'en-IN-Wavenet-E', -- Female
["en_IN_Wavenet_F"] = 'en-IN-Wavenet-F', -- Male
-- 2025 changes -- 2025 changes
["en_GB_Wavenet_A"] = 'en-GB-Wavenet-N', -- [9] FEMALE ["en_GB_Wavenet_A"] = 'en-GB-Wavenet-A', -- [9] FEMALE
["en_GB_Wavenet_B"] = 'en-GB-Wavenet-O', -- [10] MALE ["en_GB_Wavenet_B"] = 'en-GB-Wavenet-B', -- [10] MALE
["en_GB_Wavenet_C"] = 'en-GB-Wavenet-N', -- [11] FEMALE ["en_GB_Wavenet_C"] = 'en-GB-Wavenet-C', -- [11] FEMALE
["en_GB_Wavenet_D"] = 'en-GB-Wavenet-O', -- [12] MALE ["en_GB_Wavenet_D"] = 'en-GB-Wavenet-D', -- [12] MALE
["en_GB_Wavenet_F"] = 'en-GB-Wavenet-N', -- [13] FEMALE ["en_GB_Wavenet_F"] = 'en-GB-Wavenet-F', -- [13] FEMALE
["en_GB_Wavenet_O"] = 'en-GB-Wavenet-O', -- [12] MALE ["en_GB_Wavenet_O"] = 'en-GB-Wavenet-O', -- [12] MALE
["en_GB_Wavenet_N"] = 'en-GB-Wavenet-N', -- [13] FEMALE ["en_GB_Wavenet_N"] = 'en-GB-Wavenet-N', -- [13] FEMALE
["en_US_Wavenet_A"] = 'en-US-Wavenet-A', -- [14] MALE -- US
["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- [15] MALE ["en_US_Wavenet_A"] = 'en-US-Wavenet-A', -- Male
["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- [16] FEMALE ["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- Male
["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- [17] MALE ["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- Female
["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- [18] FEMALE ["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- Male
["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- [19] FEMALE ["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- Female
["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- [20] FEMALE ["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- Female
["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- [21] FEMALE ["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- Female
["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- [22] MALE ["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- Female
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- [23] MALE ["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- Male
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- Male
-- 2025 catalog changes -- 2025 catalog changes
["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-F", -- Female ["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-F", -- Female
["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-G", -- Male ["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-G", -- Male
@ -532,14 +541,15 @@ MSRS.Voices = {
["fr_FR_Wavenet_G"] = "fr-FR-Wavenet-G", -- Male ["fr_FR_Wavenet_G"] = "fr-FR-Wavenet-G", -- Male
["fr_FR_Wavenet_F"] = "fr-FR-Wavenet-F", -- Female ["fr_FR_Wavenet_F"] = "fr-FR-Wavenet-F", -- Female
-- 2025 catalog changes -- 2025 catalog changes
["de_DE_Wavenet_A"] = "de-DE-Wavenet-G", -- Female ["de_DE_Wavenet_A"] = 'de-DE-Wavenet-A', -- Female
["de_DE_Wavenet_B"] = "de-DE-Wavenet-H", -- Male ["de_DE_Wavenet_B"] = 'de-DE-Wavenet-B', -- Male
["de_DE_Wavenet_C"] = "de-DE-Wavenet-G", -- Female ["de_DE_Wavenet_C"] = 'de-DE-Wavenet-C', -- Female
["de_DE_Wavenet_D"] = "de-DE-Wavenet-H", -- Male ["de_DE_Wavenet_D"] = 'de-DE-Wavenet-D', -- Male
["de_DE_Wavenet_E"] = "de-DE-Wavenet-H", -- Male ["de_DE_Wavenet_E"] = 'de-DE-Wavenet-E', -- Male
["de_DE_Wavenet_F"] = "de-DE-Wavenet-G", -- Female ["de_DE_Wavenet_F"] = 'de-DE-Wavenet-F', -- Female
["de_DE_Wavenet_H"] = "de-DE-Wavenet-H", -- Male ["de_DE_Wavenet_G"] = 'de-DE-Wavenet-G', -- Female
["de_DE_Wavenet_G"] = "de-DE-Wavenet-G", -- Female ["de_DE_Wavenet_H"] = 'de-DE-Wavenet-H', -- Male
-- ES
["es_ES_Wavenet_B"] = "es-ES-Wavenet-E", -- Male ["es_ES_Wavenet_B"] = "es-ES-Wavenet-E", -- Male
["es_ES_Wavenet_C"] = "es-ES-Wavenet-F", -- Female ["es_ES_Wavenet_C"] = "es-ES-Wavenet-F", -- Female
["es_ES_Wavenet_D"] = "es-ES-Wavenet-E", -- Female ["es_ES_Wavenet_D"] = "es-ES-Wavenet-E", -- Female
@ -553,6 +563,134 @@ MSRS.Voices = {
["it_IT_Wavenet_E"] = "it-IT-Wavenet-E", -- Female ["it_IT_Wavenet_E"] = "it-IT-Wavenet-E", -- Female
["it_IT_Wavenet_F"] = "it-IT-Wavenet-F", -- Male ["it_IT_Wavenet_F"] = "it-IT-Wavenet-F", -- Male
} , } ,
Chirp3HD = {
["en_GB_Chirp3_HD_Aoede"] = 'en-GB-Chirp3-HD-Aoede', -- Female
["en_GB_Chirp3_HD_Charon"] = 'en-GB-Chirp3-HD-Charon', -- Male
["en_GB_Chirp3_HD_Fenrir"] = 'en-GB-Chirp3-HD-Fenrir', -- Male
["en_GB_Chirp3_HD_Kore"] = 'en-GB-Chirp3-HD-Kore', -- Female
["en_GB_Chirp3_HD_Leda"] = 'en-GB-Chirp3-HD-Leda', -- Female
["en_GB_Chirp3_HD_Orus"] = 'en-GB-Chirp3-HD-Orus', -- Male
["en_GB_Chirp3_HD_Puck"] = 'en-GB-Chirp3-HD-Puck', -- Male
["en_GB_Chirp3_HD_Zephyr"] = 'en-GB-Chirp3-HD-Zephyr', -- Female
--["de_DE_Chirp3_HD_Aoede"] = 'de-DE-Chirp3-HD-Aoede', -- Female (Datenfehler im Original)
["en_US_Chirp3_HD_Charon"] = 'en-US-Chirp3-HD-Charon', -- Male
["en_US_Chirp3_HD_Fenrir"] = 'en-US-Chirp3-HD-Fenrir', -- Male
["en_US_Chirp3_HD_Kore"] = 'en-US-Chirp3-HD-Kore', -- Female
["en_US_Chirp3_HD_Leda"] = 'en-US-Chirp3-HD-Leda', -- Female
["en_US_Chirp3_HD_Orus"] = 'en-US-Chirp3-HD-Orus', -- Male
["en_US_Chirp3_HD_Puck"] = 'en-US-Chirp3-HD-Puck', -- Male
--["de_DE_Chirp3_HD_Zephyr"] = 'de-DE-Chirp3-HD-Zephyr', -- Female (Datenfehler im Original)
-- DE
["de_DE_Chirp3_HD_Aoede"] = 'de-DE-Chirp3-HD-Aoede', -- Female
["de_DE_Chirp3_HD_Charon"] = 'de-DE-Chirp3-HD-Charon', -- Male
["de_DE_Chirp3_HD_Fenrir"] = 'de-DE-Chirp3-HD-Fenrir', -- Male
["de_DE_Chirp3_HD_Kore"] = 'de-DE-Chirp3-HD-Kore', -- Female
["de_DE_Chirp3_HD_Leda"] = 'de-DE-Chirp3-HD-Leda', -- Female
["de_DE_Chirp3_HD_Orus"] = 'de-DE-Chirp3-HD-Orus', -- Male
["de_DE_Chirp3_HD_Puck"] = 'de-DE-Chirp3-HD-Puck', -- Male
["de_DE_Chirp3_HD_Zephyr"] = 'de-DE-Chirp3-HD-Zephyr', -- Female
-- AU
["en_AU_Chirp3_HD_Aoede"] = 'en-AU-Chirp3-HD-Aoede', -- Female
["en_AU_Chirp3_HD_Charon"] = 'en-AU-Chirp3-HD-Charon', -- Male
["en_AU_Chirp3_HD_Fenrir"] = 'en-AU-Chirp3-HD-Fenrir', -- Male
["en_AU_Chirp3_HD_Kore"] = 'en-AU-Chirp3-HD-Kore', -- Female
["en_AU_Chirp3_HD_Leda"] = 'en-AU-Chirp3-HD-Leda', -- Female
["en_AU_Chirp3_HD_Orus"] = 'en-AU-Chirp3-HD-Orus', -- Male
["en_AU_Chirp3_HD_Puck"] = 'en-AU-Chirp3-HD-Puck', -- Male
["en_AU_Chirp3_HD_Zephyr"] = 'en-AU-Chirp3-HD-Zephyr', -- Female
-- IN
["en_IN_Chirp3_HD_Aoede"] = 'en-IN-Chirp3-HD-Aoede', -- Female
["en_IN_Chirp3_HD_Charon"] = 'en-IN-Chirp3-HD-Charon', -- Male
["en_IN_Chirp3_HD_Fenrir"] = 'en-IN-Chirp3-HD-Fenrir', -- Male
["en_IN_Chirp3_HD_Kore"] = 'en-IN-Chirp3-HD-Kore', -- Female
["en_IN_Chirp3_HD_Leda"] = 'en-IN-Chirp3-HD-Leda', -- Female
["en_IN_Chirp3_HD_Orus"] = 'en-IN-Chirp3-HD-Orus', -- Male
},
ChirpHD = {
["en_US_Chirp_HD_D"] = 'en-US-Chirp-HD-D', -- Male
["en_US_Chirp_HD_F"] = 'en-US-Chirp-HD-F', -- Female
["en_US_Chirp_HD_O"] = 'en-US-Chirp-HD-O', -- Female
-- DE
["de_DE_Chirp_HD_D"] = 'de-DE-Chirp-HD-D', -- Male
["de_DE_Chirp_HD_F"] = 'de-DE-Chirp-HD-F', -- Female
["de_DE_Chirp_HD_O"] = 'de-DE-Chirp-HD-O', -- Female
-- AU
["en_AU_Chirp_HD_D"] = 'en-AU-Chirp-HD-D', -- Male
["en_AU_Chirp_HD_F"] = 'en-AU-Chirp-HD-F', -- Female
["en_AU_Chirp_HD_O"] = 'en-AU-Chirp-HD-O', -- Female
-- IN
["en_IN_Chirp_HD_D"] = 'en-IN-Chirp-HD-D', -- Male
["en_IN_Chirp_HD_F"] = 'en-IN-Chirp-HD-F', -- Female
["en_IN_Chirp_HD_O"] = 'en-IN-Chirp-HD-O', -- Female
},
},
Neural2 = {
["en_GB_Neural2_A"] = 'en-GB-Neural2-A', -- Female
["en_GB_Neural2_B"] = 'en-GB-Neural2-B', -- Male
["en_GB_Neural2_C"] = 'en-GB-Neural2-C', -- Female
["en_GB_Neural2_D"] = 'en-GB-Neural2-D', -- Male
["en_GB_Neural2_F"] = 'en-GB-Neural2-F', -- Female
["en_GB_Neural2_N"] = 'en-GB-Neural2-N', -- Female
["en_GB_Neural2_O"] = 'en-GB-Neural2-O', -- Male
-- US
["en_US_Neural2_A"] = 'en-US-Neural2-A', -- Male
["en_US_Neural2_C"] = 'en-US-Neural2-C', -- Female
["en_US_Neural2_D"] = 'en-US-Neural2-D', -- Male
["en_US_Neural2_E"] = 'en-US-Neural2-E', -- Female
["en_US_Neural2_F"] = 'en-US-Neural2-F', -- Female
["en_US_Neural2_G"] = 'en-US-Neural2-G', -- Female
["en_US_Neural2_H"] = 'en-US-Neural2-H', -- Female
["en_US_Neural2_I"] = 'en-US-Neural2-I', -- Male
["en_US_Neural2_J"] = 'en-US-Neural2-J', -- Male
-- DE
["de_DE_Neural2_G"] = 'de-DE-Neural2-G', -- Female
["de_DE_Neural2_H"] = 'de-DE-Neural2-H', -- Male
-- AU
["en_AU_Neural2_A"] = 'en-AU-Neural2-A', -- Female
["en_AU_Neural2_B"] = 'en-AU-Neural2-B', -- Male
["en_AU_Neural2_C"] = 'en-AU-Neural2-C', -- Female
["en_AU_Neural2_D"] = 'en-AU-Neural2-D', -- Male
-- IN
["en_IN_Neural2_A"] = 'en-IN-Neural2-A', -- Female
["en_IN_Neural2_B"] = 'en-IN-Neural2-B', -- Male
["en_IN_Neural2_C"] = 'en-IN-Neural2-C', -- Male
["en_IN_Neural2_D"] = 'en-IN-Neural2-D', -- Female
},
News = {
["en_GB_News_G"] = 'en-GB-News-G', -- Female
["en_GB_News_H"] = 'en-GB-News-H', -- Female
["en_GB_News_I"] = 'en-GB-News-I', -- Female
["en_GB_News_J"] = 'en-GB-News-J', -- Male
["en_GB_News_K"] = 'en-GB-News-K', -- Male
["en_GB_News_L"] = 'en-GB-News-L', -- Male
["en_GB_News_M"] = 'en-GB-News-M', -- Male
-- US
["en_US_News_K"] = 'en-US-News-K', -- Female
["en_US_News_L"] = 'en-US-News-L', -- Female
["en_US_News_N"] = 'en-US-News-N', -- Male
-- AU
["en_AU_News_E"] = 'en-AU-News-E', -- Female
["en_AU_News_F"] = 'en-AU-News-F', -- Female
["en_AU_News_G"] = 'en-AU-News-G', -- Male
},
Casual = {
["en_US_Casual_K"] = 'en-US-Casual-K', -- Male
},
Polyglot = {
["en_US_Polyglot_1"] = 'en-US-Polyglot-1', -- Male
["de_DE_Polyglot_1"] = 'de-DE-Polyglot-1', -- Male
["en_AU_Polyglot_1"] = 'en-AU-Polyglot-1', -- Male
},
Studio = {
-- Englisch (UK) - Studio
["en_GB_Studio_B"] = 'en-GB-Studio-B', -- Male
["en_GB_Studio_C"] = 'en-GB-Studio-C', -- Female
-- Englisch (USA) - Studio
["en_US_Studio_O"] = 'en-US-Studio-O', -- Female
["en_US_Studio_Q"] = 'en-US-Studio-Q', -- Male
-- DE
["de_DE_Studio_B"] = 'de-DE-Studio-B', -- Male
["de_DE_Studio_C"] = 'de-DE-Studio-C', -- Female
}, },
} }

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

@ -1586,12 +1586,12 @@ function UTILS.HdgDiff(h1, h2)
return math.abs(delta) return math.abs(delta)
end end
--- Returns the heading from one vec3 to another vec3. --- Returns the heading from one vec2/vec3 to another vec2/vec3.
-- @param DCS#Vec3 a From vec3. -- @param DCS#Vec3 a From Vec2 or Vec3.
-- @param DCS#Vec3 b To vec3. -- @param DCS#Vec3 b To Vec2 or Vec3.
-- @return #number Heading in degrees. -- @return #number Heading in degrees.
function UTILS.HdgTo(a, b) 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 dx=b.x-a.x
local heading=math.deg(math.atan2(dz, dx)) local heading=math.deg(math.atan2(dz, dx))
if heading < 0 then if heading < 0 then
@ -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,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 Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
local ReturnObjects = {} local ReturnObjects = {}
-- Spawn FARP -- many FARPs
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP" local NumberPads = NumberPads or 1
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS" local SpacingX = SpacingX or 100
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart) local SpacingY = SpacingY or 100
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name) local FarpVec2 = Coordinate:GetVec2()
table.insert(ReturnObjects,spawnedfarp)
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 -- 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 +4349,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
@ -4598,16 +4714,16 @@ function UTILS.DoStringIn(State,DoString)
return net.dostring_in(State,DoString) return net.dostring_in(State,DoString)
end end
--- Show a picture on the screen --- Show a picture on the screen to all
-- @param #string FileName File name of the picture -- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10 -- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false -- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0 -- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, defaults to 1 (left), can be 0 (center) or 2 (right) -- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, defaults to 1 (top), can be 0 (center) or 2 (bottom) -- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100 -- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, defaults to 0 (percent), can be 1 (pixels) -- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPicture(FileName, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits) function UTILS.ShowPictureToAll(FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false ClearView = ClearView or false
StartDelay = StartDelay or 0 StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1 HorizontalAlign = HorizontalAlign or 1
@ -4617,16 +4733,142 @@ function UTILS.ShowPicture(FileName, Duration, ClearView, StartDelay, Horizontal
if ClearView then ClearView = "true" else ClearView = "false" end if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture(getValueResourceByKey(\"%s\"), %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", FileName, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)) net.dostring_in("mission", string.format("a_out_picture(\"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Coalition
-- @param #number Coalition Coalition ID, can be coalition.side.BLUE, coalition.side.RED or coalition.side.NEUTRAL
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToCoalition(Coalition, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
local coalName = string.lower(UTILS.GetCoalitionName(Coalition))
net.dostring_in("mission", string.format("a_out_picture_s(\"%s\", \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", coalName, FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Country
-- @param #number Country Country ID, can be country.id.USA, country.id.RUSSIA, etc.
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToCountry(Country, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture_c(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Country, FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Group
-- @param Wrapper.Group#GROUP Group Group to show the picture to
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToGroup(Group, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture_g(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Group:GetID(), FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Unit
-- @param Wrapper.Unit#UNIT Unit Unit to show the picture to
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToUnit(Unit, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture_u(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Unit:GetID(), FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Load a mission file. This will replace the current mission with the one given carrying along the online clients.
-- @param #string FileName Mission filename
function UTILS.LoadMission(FileName)
net.dostring_in("mission", string.format("a_load_mission(\"%s\")", FileName))
end
--- Set the mission briefing for a coalition.
-- @param #number Coalition Briefing coalition ID, can be coalition.side.BLUE, coalition.side.RED or coalition.side.NEUTRAL
-- @param #string Text Briefing text, can contain newlines, will be converted formatted properly for DCS
-- @param #string Picture Picture file path, can be a file in the DEFAULT folder inside the .miz
function UTILS.SetMissionBriefing(Coalition, Text, Picture)
Text = Text or ""
Text = Text:gsub("\n", "\\n")
Picture = Picture or ""
local coalName = string.lower(UTILS.GetCoalitionName(Coalition))
net.dostring_in("mission", string.format("a_set_briefing(\"%s\", \"%s\", \"%s\")", coalName, Picture, Text))
end end
--- Show a helper gate at a DCS#Vec3 position --- Show a helper gate at a DCS#Vec3 position
-- @param DCS#Vec3 pos The position -- @param DCS#Vec3 pos The position
-- @param number heading Heading in degrees, can be 0..359 degrees -- @param #number heading Heading in degrees, can be 0..359 degrees
function UTILS.ShowHelperGate(pos, heading) function UTILS.ShowHelperGate(pos, heading)
net.dostring_in("mission",string.format("a_show_helper_gate(%s, %s, %s, %f)", pos.x, pos.y, pos.z, math.rad(heading))) net.dostring_in("mission",string.format("a_show_helper_gate(%s, %s, %s, %f)", pos.x, pos.y, pos.z, math.rad(heading)))
end end
--- Show a helper gate for a unit.
-- @param Wrapper.Unit#UNIT Unit The unit to show the gate for
-- @param #number Flag Helper gate flag
function UTILS.ShowHelperGateForUnit(Unit, Flag)
net.dostring_in("mission",string.format("a_show_route_gates_for_unit(%d, \"%d\")", Unit:GetID(), Flag))
end
--- Set the carrier illumination mode. -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
-- @param #number UnitID Carrier unit ID ( UNIT:GetID() )
-- @param #number Mode Illumination mode, can be -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
function UTILS.SetCarrierIlluminationMode(UnitID, Mode)
net.dostring_in("mission",string.format("a_set_carrier_illumination_mode(%d, %d)", UnitID, Mode))
end
--- Shell a zone, zone must ME created --- Shell a zone, zone must ME created
-- @param #string name The name of the ME created zone -- @param #string name The name of the ME created zone
-- @param #number power Equals kg of TNT, e.g. 75 -- @param #number power Equals kg of TNT, e.g. 75
@ -4745,3 +4987,161 @@ function UTILS.FindNearestPointOnCircle(Vec1,Radius,Vec2)
return {x=qx, y=qy} return {x=qx, y=qy}
end 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.
-- @param #table Positions A table of DCS#Vec2 or DCS#Vec3, can be a units table from the group template.
-- @param DCS#Vec2 Anchor (Optional) DCS#Vec2 or DCS#Vec3 as anchor point to calculate offset of the units.
-- @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.
function UTILS.ValidateAndRepositionGroundUnits(Positions, Anchor, MaxRadius, Spacing)
local units = Positions
Anchor = Anchor or UTILS.GetCenterPoint(units)
local gPos = { x = Anchor.x, y = Anchor.z or Anchor.y }
local maxRadius = 0
local unitCount = 0
for _, unit in pairs(units) do
local pos = { x = unit.x, y = unit.z or unit.y }
local dist = UTILS.VecDist2D(pos, gPos)
if dist > maxRadius then
maxRadius = dist
end
unitCount = unitCount + 1
end
maxRadius = MaxRadius or math.max(maxRadius * 2, 10)
local spacing = Spacing or math.max(maxRadius * 0.05, 5)
if unitCount > 0 and maxRadius > 5 then
local spots = UTILS.GetSimpleZones(UTILS.Vec2toVec3(gPos), maxRadius, spacing, 1000)
if spots and #spots > 0 then
local validSpots = {}
for _, spot in pairs(spots) do -- Disposition sometimes returns points on roads, hence this filter.
if land.getSurfaceType(spot) == land.SurfaceType.LAND then
table.insert(validSpots, spot)
end
end
spots = validSpots
end
local step = spacing
for _, unit in pairs(units) do
local pos = { x = unit.x, y = unit.z or unit.y }
local isOnLand = land.getSurfaceType(pos) == land.SurfaceType.LAND
local isValid = false
if spots and #spots > 0 then
local si = 1
local sid = 0
local closestDist = 100000000
local closestSpot
for _, spot in pairs(spots) do
local dist = UTILS.VecDist2D(pos, spot)
if dist < closestDist then
closestDist = dist
closestSpot = spot
sid = si
end
si = si + 1
end
if closestSpot then
if closestDist >= spacing then
pos = closestSpot
end
isValid = true
table.remove(spots, sid)
end
end
-- Failsafe calculation
if not isValid and not isOnLand then
local h = UTILS.HdgTo(pos, gPos)
local retries = 0
while not isValid and retries < 500 do
local dist = UTILS.VecDist2D(pos, gPos)
pos = UTILS.Vec2Translate(pos, step, h)
local skip = false
for _, unit2 in pairs(units) do
if unit ~= unit2 then
local pos2 = { x = unit2.x, y = unit2.z or unit2.y }
local dist2 = UTILS.VecDist2D(pos, pos2)
if dist2 < 12 then
isValid = false
skip = true
break
end
end
end
if not skip and dist > step and land.getSurfaceType(pos) == land.SurfaceType.LAND then
isValid = true
break
elseif dist <= step then
break
end
retries = retries + 1
end
end
if isValid then
unit.x = pos.x
if unit.z then
unit.z = pos.y
else
unit.y = pos.y
end
end
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,51 +703,56 @@ AIRBASE.SouthAtlantic={
--- Airbases of the Sinai map: --- Airbases of the Sinai map:
-- --
-- * AIRBASE.Sinai.Abu_Rudeis -- * AIRBASE.SinaiMap.Abu_Rudeis
-- * AIRBASE.Sinai.Abu_Suwayr -- * AIRBASE.SinaiMap.Abu_Suwayr
-- * AIRBASE.Sinai.Al_Bahr_al_Ahmar -- * AIRBASE.SinaiMap.Al_Bahr_al_Ahmar
-- * AIRBASE.Sinai.Al_Ismailiyah -- * AIRBASE.SinaiMap.Al_Ismailiyah
-- * AIRBASE.Sinai.Al_Khatatbah -- * AIRBASE.SinaiMap.Al_Khatatbah
-- * AIRBASE.Sinai.Al_Mansurah -- * AIRBASE.SinaiMap.Al_Mansurah
-- * AIRBASE.Sinai.Al_Rahmaniyah_Air_Base -- * AIRBASE.SinaiMap.Al_Rahmaniyah_Air_Base
-- * AIRBASE.Sinai.As_Salihiyah -- * AIRBASE.SinaiMap.As_Salihiyah
-- * AIRBASE.Sinai.AzZaqaziq -- * AIRBASE.SinaiMap.AzZaqaziq
-- * AIRBASE.Sinai.Baluza -- * AIRBASE.SinaiMap.Baluza
-- * AIRBASE.Sinai.Ben_Gurion -- * AIRBASE.SinaiMap.Ben_Gurion
-- * AIRBASE.Sinai.Beni_Suef -- * AIRBASE.SinaiMap.Beni_Suef
-- * AIRBASE.Sinai.Bilbeis_Air_Base -- * AIRBASE.SinaiMap.Bilbeis_Air_Base
-- * AIRBASE.Sinai.Bir_Hasanah -- * AIRBASE.SinaiMap.Bir_Hasanah
-- * AIRBASE.Sinai.Birma_Air_Base -- * AIRBASE.SinaiMap.Birma_Air_Base
-- * AIRBASE.Sinai.Borg_El_Arab_International_Airport -- * AIRBASE.SinaiMap.Borg_El_Arab_International_Airport
-- * AIRBASE.Sinai.Cairo_International_Airport -- * AIRBASE.SinaiMap.Cairo_International_Airport
-- * AIRBASE.Sinai.Cairo_West -- * AIRBASE.SinaiMap.Cairo_West
-- * AIRBASE.Sinai.Difarsuwar_Airfield -- * AIRBASE.SinaiMap.Damascus_Intl
-- * AIRBASE.Sinai.El_Arish -- * AIRBASE.SinaiMap.Difarsuwar_Airfield
-- * AIRBASE.Sinai.El_Gora -- * AIRBASE.SinaiMap.El_Arish
-- * AIRBASE.Sinai.El_Minya -- * AIRBASE.SinaiMap.El_Gora
-- * AIRBASE.Sinai.Fayed -- * AIRBASE.SinaiMap.El_Minya
-- * AIRBASE.Sinai.Gebel_El_Basur_Air_Base -- * AIRBASE.SinaiMap.Fayed
-- * AIRBASE.Sinai.Hatzerim -- * AIRBASE.SinaiMap.Gebel_El_Basur_Air_Base
-- * AIRBASE.Sinai.Hatzor -- * AIRBASE.SinaiMap.Hatzerim
-- * AIRBASE.Sinai.Hurghada_International_Airport -- * AIRBASE.SinaiMap.Hatzor
-- * AIRBASE.Sinai.Inshas_Airbase -- * AIRBASE.SinaiMap.Hurghada_International_Airport
-- * AIRBASE.Sinai.Jiyanklis_Air_Base -- * AIRBASE.SinaiMap.Inshas_Airbase
-- * AIRBASE.Sinai.Kedem -- * AIRBASE.SinaiMap.Jiyanklis_Air_Base
-- * AIRBASE.Sinai.Kibrit_Air_Base -- * AIRBASE.SinaiMap.Kedem
-- * AIRBASE.Sinai.Kom_Awshim -- * AIRBASE.SinaiMap.Kibrit_Air_Base
-- * AIRBASE.Sinai.Melez -- * AIRBASE.SinaiMap.Kom_Awshim
-- * AIRBASE.Sinai.Nevatim -- * AIRBASE.SinaiMap.Melez
-- * AIRBASE.Sinai.Ovda -- * AIRBASE.SinaiMap.Mezzeh_Air_Base
-- * AIRBASE.Sinai.Palmachim -- * AIRBASE.SinaiMap.Nevatim
-- * AIRBASE.Sinai.Quwaysina -- * AIRBASE.SinaiMap.Ovda
-- * AIRBASE.Sinai.Ramon_Airbase -- * AIRBASE.SinaiMap.Palmachim
-- * AIRBASE.Sinai.Ramon_International_Airport -- * AIRBASE.SinaiMap.Quwaysina
-- * AIRBASE.Sinai.Sde_Dov -- * AIRBASE.SinaiMap.Rafic_Hariri_Intl
-- * AIRBASE.Sinai.Sharm_El_Sheikh_International_Airport -- * AIRBASE.SinaiMap.Ramat_David
-- * AIRBASE.Sinai.St_Catherine -- * AIRBASE.SinaiMap.Ramon_Airbase
-- * AIRBASE.Sinai.Tel_Nof -- * AIRBASE.SinaiMap.Ramon_International_Airport
-- * AIRBASE.Sinai.Wadi_Abu_Rish -- * AIRBASE.SinaiMap.Sde_Dov
-- * AIRBASE.Sinai.Wadi_al_Jandali -- * 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 -- @field Sinai
AIRBASE.Sinai = { AIRBASE.Sinai = {
@ -769,6 +774,7 @@ AIRBASE.Sinai = {
["Borg_El_Arab_International_Airport"] = "Borg El Arab International Airport", ["Borg_El_Arab_International_Airport"] = "Borg El Arab International Airport",
["Cairo_International_Airport"] = "Cairo International Airport", ["Cairo_International_Airport"] = "Cairo International Airport",
["Cairo_West"] = "Cairo West", ["Cairo_West"] = "Cairo West",
["Damascus_Intl"] = "Damascus Intl",
["Difarsuwar_Airfield"] = "Difarsuwar Airfield", ["Difarsuwar_Airfield"] = "Difarsuwar Airfield",
["El_Arish"] = "El Arish", ["El_Arish"] = "El Arish",
["El_Gora"] = "El Gora", ["El_Gora"] = "El Gora",
@ -784,15 +790,19 @@ AIRBASE.Sinai = {
["Kibrit_Air_Base"] = "Kibrit Air Base", ["Kibrit_Air_Base"] = "Kibrit Air Base",
["Kom_Awshim"] = "Kom Awshim", ["Kom_Awshim"] = "Kom Awshim",
["Melez"] = "Melez", ["Melez"] = "Melez",
["Mezzeh_Air_Base"] = "Mezzeh Air Base",
["Nevatim"] = "Nevatim", ["Nevatim"] = "Nevatim",
["Ovda"] = "Ovda", ["Ovda"] = "Ovda",
["Palmachim"] = "Palmachim", ["Palmachim"] = "Palmachim",
["Quwaysina"] = "Quwaysina", ["Quwaysina"] = "Quwaysina",
["Rafic_Hariri_Intl"] = "Rafic Hariri Intl",
["Ramat_David"] = "Ramat David",
["Ramon_Airbase"] = "Ramon Airbase", ["Ramon_Airbase"] = "Ramon Airbase",
["Ramon_International_Airport"] = "Ramon International Airport", ["Ramon_International_Airport"] = "Ramon International Airport",
["Sde_Dov"] = "Sde Dov", ["Sde_Dov"] = "Sde Dov",
["Sharm_El_Sheikh_International_Airport"] = "Sharm El Sheikh International Airport", ["Sharm_El_Sheikh_International_Airport"] = "Sharm El Sheikh International Airport",
["St_Catherine"] = "St Catherine", ["St_Catherine"] = "St Catherine",
["Tabuk"] = "Tabuk",
["Tel_Nof"] = "Tel Nof", ["Tel_Nof"] = "Tel Nof",
["Wadi_Abu_Rish"] = "Wadi Abu Rish", ["Wadi_Abu_Rish"] = "Wadi Abu Rish",
["Wadi_al_Jandali"] = "Wadi al Jandali", ["Wadi_al_Jandali"] = "Wadi al Jandali",
@ -923,37 +933,51 @@ AIRBASE.Afghanistan = {
--- Airbases of the Iraq map --- 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_Asad_Airbase
-- * AIRBASE.Iraq.Al_Kut_Airbase
-- * AIRBASE.Iraq.Al_Sahra_Airport
-- * AIRBASE.Iraq.Al_Salam_Airbase -- * AIRBASE.Iraq.Al_Salam_Airbase
-- * AIRBASE.Iraq.Balad_Airbase -- * AIRBASE.Iraq.Al_Taji_Airport
-- * AIRBASE.Iraq.Kirkuk_International_Airport
-- * AIRBASE.Iraq.Bashur_Airport
-- * AIRBASE.Iraq.Al_Taquddum_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.K1_Base
-- * AIRBASE.Iraq.Kirkuk_International_Airport
-- * AIRBASE.Iraq.Mosul_International_Airport
-- * AIRBASE.Iraq.Qayyarah_Airfield_West
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
-- --
-- @field Iraq -- @field Iraq
AIRBASE.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_Asad_Airbase"] = "Al-Asad Airbase",
["Al_Kut_Airport"] = "Al-Kut Airport",
["Al_Sahra_Airport"] = "Al-Sahra Airport",
["Al_Salam_Airbase"] = "Al-Salam Airbase", ["Al_Salam_Airbase"] = "Al-Salam Airbase",
["Balad_Airbase"] = "Balad Airbase", ["Al_Taji_Airport"] = "Al-Taji Airport",
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
["Bashur_Airport"] = "Bashur Airport",
["Al_Taquddum_Airport"] = "Al-Taquddum 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", ["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 --- Airbases of the Germany Cold War map
-- * AIRBASE.GermanyCW.Airracing_Frankfurt -- * AIRBASE.GermanyCW.Airracing_Frankfurt
-- * AIRBASE.GermanyCW.Airracing_Frankfurt -- * AIRBASE.GermanyCW.Airracing_Frankfurt
@ -1550,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

@ -231,6 +231,7 @@ GROUP.Attribute = {
GROUND_AAA="Ground_AAA", GROUND_AAA="Ground_AAA",
GROUND_SAM="Ground_SAM", GROUND_SAM="Ground_SAM",
GROUND_SHORAD="Ground_SHORAD", GROUND_SHORAD="Ground_SHORAD",
GROUND_BALLISTICMISSILE="Ground_BallisticMissile",
GROUND_OTHER="Ground_OtherGround", GROUND_OTHER="Ground_OtherGround",
NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier", NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
NAVAL_WARSHIP="Naval_WarShip", NAVAL_WARSHIP="Naval_WarShip",
@ -2231,6 +2232,10 @@ function GROUP:Respawn( Template, Reset )
--UTILS.PrintTableToLog(Template) --UTILS.PrintTableToLog(Template)
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(Template.units)
end
-- Spawn new group. -- Spawn new group.
self:ScheduleOnce(0.1,_DATABASE.Spawn,_DATABASE,Template) self:ScheduleOnce(0.1,_DATABASE.Spawn,_DATABASE,Template)
--_DATABASE:Spawn(Template) --_DATABASE:Spawn(Template)
@ -2639,6 +2644,8 @@ function GROUP:GetAttribute()
local artillery=self:HasAttribute("Artillery") local artillery=self:HasAttribute("Artillery")
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") or self:HasAttribute("Tanks") 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 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 ewr=self:HasAttribute("EWR")
local ifv=self:HasAttribute("IFV") local ifv=self:HasAttribute("IFV")
local sam=self:HasAttribute("SAM elements") or self:HasAttribute("Optical Tracker") local sam=self:HasAttribute("SAM elements") or self:HasAttribute("Optical Tracker")
@ -2680,6 +2687,8 @@ function GROUP:GetAttribute()
attribute=GROUP.Attribute.GROUND_SAM attribute=GROUP.Attribute.GROUND_SAM
elseif aaa then elseif aaa then
attribute=GROUP.Attribute.GROUND_AAA attribute=GROUP.Attribute.GROUND_AAA
elseif artillery and ballisticMissile then
attribute=GROUP.Attribute.GROUND_BALLISTICMISSILE
elseif artillery then elseif artillery then
attribute=GROUP.Attribute.GROUND_ARTILLERY attribute=GROUP.Attribute.GROUND_ARTILLERY
elseif tank then elseif tank then
@ -3192,3 +3201,60 @@ function GROUP:IsAAA()
end end
return isAAA return isAAA
end 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. -- Get the current position.
local PositionableVec3 = self:GetVec3() local PositionableVec3 = self:GetVec3()
if self.coordinate then if PositionableVec3 then
-- Update COORDINATE from 3D vector. if self.coordinate then
self.coordinate:UpdateFromVec3( PositionableVec3 ) -- Update COORDINATE from 3D vector.
else self.coordinate:UpdateFromVec3( PositionableVec3 )
-- New COORDINATE. else
self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 ) -- New COORDINATE.
end self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
end
return self.coordinate return self.coordinate
end
end end
-- Error message. -- Error message.
@ -1863,6 +1865,7 @@ do -- Cargo
["HL_DSHK"] = 6*POSITIONABLE.DefaultInfantryWeight, ["HL_DSHK"] = 6*POSITIONABLE.DefaultInfantryWeight,
["CCKW_353"] = 16*POSITIONABLE.DefaultInfantryWeight, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers, ["CCKW_353"] = 16*POSITIONABLE.DefaultInfantryWeight, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers,
["MaxxPro_MRAP"] = 7*POSITIONABLE.DefaultInfantryWeight, ["MaxxPro_MRAP"] = 7*POSITIONABLE.DefaultInfantryWeight,
["Sd_Kfz_251"] = 10*POSITIONABLE.DefaultInfantryWeight,
} }
} }

View File

@ -753,7 +753,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end end
end end
else 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
end end
@ -773,7 +773,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end end
end end
else 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
end end
@ -805,7 +805,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end end
end end
else 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
end end

View File

@ -377,6 +377,10 @@ function UNIT:ReSpawnAt(Coordinate, Heading)
--self:T( SpawnGroupTemplate ) --self:T( SpawnGroupTemplate )
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(SpawnGroupTemplate.units)
end
_DATABASE:Spawn(SpawnGroupTemplate) _DATABASE:Spawn(SpawnGroupTemplate)
end end
@ -1931,3 +1935,21 @@ end
function UNIT:SetLife(Percent) function UNIT:SetLife(Percent)
net.dostring_in("mission",string.format("a_unit_set_life_percentage(%d, %f)", self:GetID(), Percent)) net.dostring_in("mission",string.format("a_unit_set_life_percentage(%d, %f)", self:GetID(), Percent))
end end
--- Set the carrier illumination mode. -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
-- @param #UNIT self
-- @param #number Mode Illumination mode, can be -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
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